Merge "Revert "InputDispatcher: Blame the window from the focus request for ANR""
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 839a2c3..649a13e 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -9,15 +9,15 @@
       ]
     },
     {
-      "name": "dumpstate_smoke_test"
-    },
-    {
       "name": "dumpstate_test"
     }
   ],
   "postsubmit": [
     {
       "name": "BugreportManagerTestCases"
+    },
+    {
+      "name": "dumpstate_smoke_test"
     }
   ],
   "imports": [
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 2c8adc7..a278a48 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -99,6 +99,8 @@
 
 static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
 static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+static constexpr const char* kMiscMirrorCePath = "/data_mirror/misc_ce";
+static constexpr const char* kMiscMirrorDePath = "/data_mirror/misc_de";
 
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
@@ -3579,16 +3581,28 @@
 
     std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create CE mirror");
+        return error("Failed to create CE data mirror");
     }
 
     std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
     if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create DE mirror");
+        return error("Failed to create DE data mirror");
+    }
+
+    std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create CE misc mirror");
+    }
+
+    std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE misc mirror");
     }
 
     auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
     auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+    auto miscCePath = StringPrintf("%s/misc_ce", create_data_path(uuid_).c_str());
+    auto miscDePath = StringPrintf("%s/misc_de", create_data_path(uuid_).c_str());
 
     if (access(cePath.c_str(), F_OK) != 0) {
         return error("Cannot access CE path: " + cePath);
@@ -3596,6 +3610,12 @@
     if (access(dePath.c_str(), F_OK) != 0) {
         return error("Cannot access DE path: " + dePath);
     }
+    if (access(miscCePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc CE path: " + cePath);
+    }
+    if (access(miscDePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc DE path: " + dePath);
+    }
 
     struct stat ceStat, mirrorCeStat;
     if (stat(cePath.c_str(), &ceStat) != 0) {
@@ -3623,6 +3643,21 @@
             MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
         return error("Failed to mount " + mirrorVolDePath);
     }
+
+    // Mount misc CE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscCePath.c_str(), mirrorVolMiscCePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscCePath);
+    }
+
+    // Mount misc DE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscDePath.c_str(), mirrorVolMiscDePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscDePath);
+    }
+
     return ok();
 }
 
@@ -3645,6 +3680,8 @@
 
     std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    std::string mirrorMiscCeVolPath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    std::string mirrorMiscDeVolPath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
 
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
 
@@ -3669,6 +3706,29 @@
     if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
         res = error("Failed to delete " + mirrorDeVolPath);
     }
+
+    // Unmount misc CE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscCeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscCeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscCeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscCeVolPath);
+    }
+
+    // Unmount misc DE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscDeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscDeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscDeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscDeVolPath);
+    }
+
     return res;
 }
 
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 214559d..16a13d6 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -776,7 +776,48 @@
     AKEYCODE_THUMBS_DOWN = 287,
     /** Used to switch current account that is consuming content.
      * May be consumed by system to switch current viewer profile. */
-    AKEYCODE_PROFILE_SWITCH = 288
+    AKEYCODE_PROFILE_SWITCH = 288,
+    /** Video Application key #1. */
+    AKEYCODE_VIDEO_APP_1 = 289,
+    /** Video Application key #2. */
+    AKEYCODE_VIDEO_APP_2 = 290,
+    /** Video Application key #3. */
+    AKEYCODE_VIDEO_APP_3 = 291,
+    /** Video Application key #4. */
+    AKEYCODE_VIDEO_APP_4 = 292,
+    /** Video Application key #5. */
+    AKEYCODE_VIDEO_APP_5 = 293,
+    /** Video Application key #6. */
+    AKEYCODE_VIDEO_APP_6 = 294,
+    /** Video Application key #7. */
+    AKEYCODE_VIDEO_APP_7 = 295,
+    /** Video Application key #8. */
+    AKEYCODE_VIDEO_APP_8 = 296,
+    /** Featured Application key #1. */
+    AKEYCODE_FEATURED_APP_1 = 297,
+    /** Featured Application key #2. */
+    AKEYCODE_FEATURED_APP_2 = 298,
+    /** Featured Application key #3. */
+    AKEYCODE_FEATURED_APP_3 = 299,
+    /** Featured Application key #4. */
+    AKEYCODE_FEATURED_APP_4 = 300,
+    /** Demo Application key #1. */
+    AKEYCODE_DEMO_APP_1 = 301,
+    /** Demo Application key #2. */
+    AKEYCODE_DEMO_APP_2 = 302,
+    /** Demo Application key #3. */
+    AKEYCODE_DEMO_APP_3 = 303,
+    /** Demo Application key #4. */
+    AKEYCODE_DEMO_APP_4 = 304,
+    /** Keyboard backlight Down key.
+     * Adjusts the keyboard backlight brightness down. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_DOWN = 305,
+    /** Keyboard backlight Up key.
+     * Adjusts the keyboard backlight brightness up. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_UP = 306,
+    /** Keyboard backlight Toggle key.
+     * Toggles the keyboard backlight on/off. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
new file mode 100644
index 0000000..c5ff03b
--- /dev/null
+++ b/include/ftl/algorithm.h
@@ -0,0 +1,71 @@
+/*
+ * 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 <algorithm>
+#include <functional>
+#include <utility>
+
+#include <ftl/optional.h>
+
+namespace android::ftl {
+
+// Adapter for std::find_if that converts the return value from iterator to optional.
+//
+//   const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
+//   assert(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }) == "cake"sv);
+//
+template <typename Container, typename Predicate, typename V = typename Container::value_type>
+constexpr auto find_if(const Container& container, Predicate&& predicate)
+    -> Optional<std::reference_wrapper<const V>> {
+  const auto it = std::find_if(std::cbegin(container), std::cend(container),
+                               std::forward<Predicate>(predicate));
+  if (it == std::cend(container)) return {};
+  return std::cref(*it);
+}
+
+// Transformers for ftl::find_if on a map-like `Container` that contains key-value pairs.
+//
+//   const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>(
+//       12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv);
+//
+//   using Map = decltype(map);
+//
+//   assert(14 == ftl::find_if(map, [](const auto& pair) {
+//                  return pair.second.size() == 3;
+//                }).transform(ftl::to_key<Map>));
+//
+//   const auto opt = ftl::find_if(map, [](const auto& pair) {
+//                      return pair.second.size() == 1;
+//                    }).transform(ftl::to_mapped_ref<Map>);
+//
+//   assert(opt);
+//   assert(opt->get() == ftl::StaticVector("tiramisu"sv));
+//
+template <typename Map, typename Pair = typename Map::value_type,
+          typename Key = typename Map::key_type>
+constexpr auto to_key(const Pair& pair) -> Key {
+  return pair.first;
+}
+
+template <typename Map, typename Pair = typename Map::value_type,
+          typename Mapped = typename Map::mapped_type>
+constexpr auto to_mapped_ref(const Pair& pair) -> std::reference_wrapper<const Mapped> {
+  return std::cref(pair.second);
+}
+
+}  // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 9148fee..98a18c9 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
-#define _LIBINPUT_DISPLAY_VIEWPORT_H
+#pragma once
 
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
@@ -144,5 +143,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_DISPLAY_VIEWPORT_H
diff --git a/include/input/Input.h b/include/input/Input.h
index a3c9f33..2dd651e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_H
-#define _LIBINPUT_INPUT_H
+#pragma once
 
 #pragma GCC system_header
 
@@ -1104,5 +1103,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_H
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index d51d6a7..415080d 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_DEVICE_H
-#define _LIBINPUT_INPUT_DEVICE_H
+#pragma once
 
 #include <android/sensor.h>
 #include <input/Input.h>
@@ -341,5 +340,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_DEVICE_H
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 2a742f9..b4374ac 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_EVENT_LABELS_H
-#define _LIBINPUT_INPUT_EVENT_LABELS_H
+#pragma once
 
 #include <input/Input.h>
 #include <android/keycodes.h>
@@ -68,4 +67,3 @@
 };
 
 } // namespace android
-#endif // _LIBINPUT_INPUT_EVENT_LABELS_H
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index dbc7bfa..1c52792 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_TRANSPORT_H
-#define _LIBINPUT_INPUT_TRANSPORT_H
+#pragma once
 
 #pragma GCC system_header
 
@@ -674,5 +673,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_TRANSPORT_H
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 1c9a5ea..dc928b8 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEY_CHARACTER_MAP_H
-#define _LIBINPUT_KEY_CHARACTER_MAP_H
+#pragma once
 
 #include <stdint.h>
 #include <list>
@@ -270,5 +269,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_CHARACTER_MAP_H
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index a6c696d..e203d19 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
-#define _LIBINPUT_KEY_LAYOUT_MAP_H
+#pragma once
 
 #include <android-base/result.h>
 #include <stdint.h>
@@ -131,5 +130,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_LAYOUT_MAP_H
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 9a3e15f..f7f960f 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEYBOARD_H
-#define _LIBINPUT_KEYBOARD_H
+#pragma once
 
 #include <input/Input.h>
 #include <input/InputDevice.h>
@@ -88,5 +87,3 @@
 extern bool isMetaKey(int32_t keyCode);
 
 } // namespace android
-
-#endif // _LIBINPUT_KEYBOARD_H
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0a75278..55f730b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <map>
+#include <optional>
 #include <set>
 #include <string>
 
@@ -28,6 +29,15 @@
 }
 
 /**
+ * Convert an optional type to string.
+ */
+template <typename T>
+std::string toString(const std::optional<T>& optional,
+                     std::string (*toString)(const T&) = constToString) {
+    return optional ? toString(*optional) : "<not set>";
+}
+
+/**
  * Convert a set of integral types to string.
  */
 template <typename T>
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index b1e3f85..28e4816 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_PROPERTY_MAP_H
-#define _UTILS_PROPERTY_MAP_H
+#pragma once
 
 #include <android-base/result.h>
 #include <utils/Tokenizer.h>
@@ -98,5 +97,3 @@
 };
 
 } // namespace android
-
-#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index eda628e..a616a95 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H
-#define _LIBINPUT_TOUCHVIDEOFRAME_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/time.h>
@@ -75,5 +74,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index 1acc2ae..f72a1bd 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VELOCITY_CONTROL_H
-#define _LIBINPUT_VELOCITY_CONTROL_H
+#pragma once
 
 #include <input/Input.h>
 #include <input/VelocityTracker.h>
@@ -98,10 +97,8 @@
     VelocityControlParameters mParameters;
 
     nsecs_t mLastMovementTime;
-    VelocityTracker::Position mRawPosition;
+    float mRawPositionX, mRawPositionY;
     VelocityTracker mVelocityTracker;
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_VELOCITY_CONTROL_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 886f1f7..6144f5b 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VELOCITY_TRACKER_H
-#define _LIBINPUT_VELOCITY_TRACKER_H
+#pragma once
 
 #include <input/Input.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
+#include <map>
+#include <set>
 
 namespace android {
 
@@ -46,18 +47,14 @@
         MAX = LEGACY,
     };
 
-    struct Position {
-        float x, y;
-    };
-
     struct Estimator {
         static const size_t MAX_DEGREE = 4;
 
         // Estimator time base.
         nsecs_t time;
 
-        // Polynomial coefficients describing motion in X and Y.
-        float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
+        // Polynomial coefficients describing motion.
+        float coeff[MAX_DEGREE + 1];
 
         // Polynomial degree (number of coefficients), or zero if no information is
         // available.
@@ -71,14 +68,40 @@
             degree = 0;
             confidence = 0;
             for (size_t i = 0; i <= MAX_DEGREE; i++) {
-                xCoeff[i] = 0;
-                yCoeff[i] = 0;
+                coeff[i] = 0;
             }
         }
     };
 
-    // Creates a velocity tracker using the specified strategy.
+    /*
+     * Contains all available velocity data from a VelocityTracker.
+     */
+    struct ComputedVelocity {
+        inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const {
+            const auto& axisVelocities = mVelocities.find(axis);
+            if (axisVelocities == mVelocities.end()) {
+                return {};
+            }
+
+            const auto& axisIdVelocity = axisVelocities->second.find(id);
+            if (axisIdVelocity == axisVelocities->second.end()) {
+                return {};
+            }
+
+            return axisIdVelocity->second;
+        }
+
+        inline void addVelocity(int32_t axis, uint32_t id, float velocity) {
+            mVelocities[axis][id] = velocity;
+        }
+
+    private:
+        std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities;
+    };
+
+    // Creates a velocity tracker using the specified strategy for each supported axis.
     // If strategy is not provided, uses the default strategy for the platform.
+    // TODO(b/32830165): support axis-specific strategies.
     VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
     ~VelocityTracker();
@@ -92,45 +115,56 @@
     void clearPointers(BitSet32 idBits);
 
     // Adds movement information for a set of pointers.
-    // The idBits bitfield specifies the pointer ids of the pointers whose positions
+    // The idBits bitfield specifies the pointer ids of the pointers whose data points
     // are included in the movement.
-    // The positions array contains position information for each pointer in order by
-    // increasing id.  Its size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
+    // The positions map contains a mapping of an axis to positions array.
+    // The positions arrays contain information for each pointer in order by increasing id.
+    // Each array's size should be equal to the number of one bits in idBits.
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::map<int32_t, std::vector<float>>& positions);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
 
-    // Gets the velocity of the specified pointer id in position units per second.
-    // Returns false and sets the velocity components to zero if there is
-    // insufficient movement information for the pointer.
-    bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
+    // Returns the velocity of the specified pointer id and axis in position units per second.
+    // Returns empty optional if there is insufficient movement information for the pointer, or if
+    // the given axis is not supported for velocity tracking.
+    std::optional<float> getVelocity(int32_t axis, uint32_t id) const;
 
-    // Gets an estimator for the recent movements of the specified pointer id.
+    // Returns a ComputedVelocity instance with all available velocity data, using the given units
+    // (reference: units == 1 means "per millisecond"), and clamping each velocity between
+    // [-maxVelocity, maxVelocity], inclusive.
+    ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
+
+    // Gets an estimator for the recent movements of the specified pointer id for the given axis.
     // Returns false and clears the estimator if there is no information available
     // about the pointer.
-    bool getEstimator(uint32_t id, Estimator* outEstimator) const;
+    bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const;
 
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId; }
 
-    // Gets a bitset containing all pointer ids from the most recent movement.
-    inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
-
 private:
     // The default velocity tracker strategy.
     // Although other strategies are available for testing and comparison purposes,
     // this is the strategy that applications will actually use.  Be very careful
     // when adjusting the default strategy because it can dramatically affect
     // (often in a bad way) the user experience.
+    // TODO(b/32830165): define default strategy per axis.
     static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
 
+    // Set of all axes supported for velocity tracking.
+    static const std::set<int32_t> SUPPORTED_AXES;
+
+    // Axes specifying location on a 2D plane (i.e. X and Y).
+    static const std::set<int32_t> PLANAR_AXES;
+
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
-    std::unique_ptr<VelocityTrackerStrategy> mStrategy;
+    std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mStrategies;
 
-    bool configureStrategy(const Strategy strategy);
+    void configureStrategy(int32_t axis, const Strategy strategy);
 
     static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
 };
@@ -149,7 +183,7 @@
     virtual void clear() = 0;
     virtual void clearPointers(BitSet32 idBits) = 0;
     virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                             const std::vector<VelocityTracker::Position>& positions) = 0;
+                             const std::vector<float>& positions) = 0;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
 };
 
@@ -181,7 +215,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -196,11 +230,9 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
     float chooseWeight(uint32_t index) const;
@@ -224,7 +256,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -233,16 +265,15 @@
         nsecs_t updateTime;
         uint32_t degree;
 
-        float xpos, xvel, xaccel;
-        float ypos, yvel, yaccel;
+        float pos, vel, accel;
     };
 
     const uint32_t mDegree;
     BitSet32 mPointerIdBits;
     State mPointerState[MAX_POINTER_ID + 1];
 
-    void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
-    void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
+    void initState(State& state, nsecs_t eventTime, float pos) const;
+    void updateState(State& state, nsecs_t eventTime, float pos) const;
     void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
 };
 
@@ -258,7 +289,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -274,11 +305,9 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
     uint32_t mIndex;
@@ -293,7 +322,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -308,11 +337,9 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
     size_t mIndex;
@@ -320,5 +347,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_VELOCITY_TRACKER_H
diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h
index 6e8e2c9..a4381ea 100644
--- a/include/input/VirtualKeyMap.h
+++ b/include/input/VirtualKeyMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VIRTUAL_KEY_MAP_H
-#define _LIBINPUT_VIRTUAL_KEY_MAP_H
+#pragma once
 
 #include <stdint.h>
 
@@ -77,5 +76,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_CHARACTER_MAP_H
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index ac4cbdf..481d704 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -407,9 +407,12 @@
         Extras* e = mExtras.load(std::memory_order_acquire);
         AutoMutex lock(e->mLock);
         if (mRecordingOn) {
+            Parcel emptyReply;
             auto transaction =
                     android::binder::debug::RecordedTransaction::fromDetails(code, flags, data,
-                                                                             *reply, err);
+                                                                             reply ? *reply
+                                                                                   : emptyReply,
+                                                                             err);
             if (transaction) {
                 if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) {
                     LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b50cfb3..bfcf39a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1318,6 +1318,13 @@
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                 if (error < NO_ERROR) reply.setError(error);
 
+                // b/238777741: clear buffer before we send the reply.
+                // Otherwise, there is a race where the client may
+                // receive the reply and send another transaction
+                // here and the space used by this transaction won't
+                // be freed for the client.
+                buffer.setDataSize(0);
+
                 constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
                 sendReply(reply, (tr.flags & kForwardReplyFlags));
             } else {
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index c0a8d74..5db3eef 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "ServiceManager"
+#define LOG_TAG "ServiceManagerCppClient"
 
 #include <binder/IServiceManager.h>
 
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index e460d2c..92d132f 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -605,6 +605,7 @@
     shared_libs: [
         "libbinder",
         "liblog",
+        "libcutils",
         "libutils",
         "libutilscallstack",
         "libbase",
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index a2ab8ab..55a3916 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -20,6 +20,7 @@
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <cutils/trace.h>
 #include <gtest/gtest.h>
 #include <utils/CallStack.h>
 
@@ -223,5 +224,10 @@
         return 1;
     }
     ::testing::InitGoogleTest(&argc, argv);
+
+    // if tracing is enabled, take in one-time cost
+    (void)ATRACE_INIT();
+    (void)ATRACE_GET_ENABLED_TAGS();
+
     return RUN_ALL_TESTS();
 }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5de08bd..6e1c8ac 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1161,8 +1161,7 @@
 // see ProcessState.cpp BINDER_VM_SIZE = 1MB.
 // This value is not exposed, but some code in the framework relies on being able to use
 // buffers near the cap size.
-// TODO(b/238777741): why do larger values, like 300K fail sometimes
-constexpr size_t kSizeBytesAlmostFull = 100'000;
+constexpr size_t kSizeBytesAlmostFull = 950'000;
 constexpr size_t kSizeBytesOverFull = 1'050'000;
 
 TEST_F(BinderLibTest, GargantuanVectorSent) {
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 47c0657..29924ff 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -28,6 +28,7 @@
 cc_library {
     name: "libfakeservicemanager",
     defaults: ["fakeservicemanager_defaults"],
+    export_include_dirs: ["include/fakeservicemanager"],
 }
 
 cc_test_host {
@@ -37,4 +38,5 @@
         "test_sm.cpp",
     ],
     static_libs: ["libgmock"],
+    local_include_dirs: ["include/fakeservicemanager"],
 }
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
similarity index 100%
rename from libs/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 8f89e7d..a25a493 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -14,6 +14,7 @@
         address: true,
     },
     srcs: [
+        "algorithm_test.cpp",
         "cast_test.cpp",
         "concat_test.cpp",
         "enum_test.cpp",
diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp
new file mode 100644
index 0000000..8052caf
--- /dev/null
+++ b/libs/ftl/algorithm_test.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 <ftl/algorithm.h>
+#include <ftl/small_map.h>
+#include <ftl/static_vector.h>
+#include <gtest/gtest.h>
+
+#include <string_view>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Algorithm, FindIf) {
+  using namespace std::string_view_literals;
+
+  const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
+  EXPECT_EQ(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }), "cake"sv);
+
+  const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>(
+      12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv);
+
+  using Map = decltype(map);
+
+  EXPECT_EQ(14, ftl::find_if(map, [](const auto& pair) {
+                  return pair.second.size() == 3;
+                }).transform(ftl::to_key<Map>));
+
+  const auto opt = ftl::find_if(map, [](const auto& pair) {
+                     return pair.second.size() == 1;
+                   }).transform(ftl::to_mapped_ref<Map>);
+
+  ASSERT_TRUE(opt);
+  EXPECT_EQ(opt->get(), ftl::StaticVector("tiramisu"sv));
+}
+
+}  // namespace android::test
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3bf2e19..3afa339 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -1083,50 +1083,6 @@
     return mLastAcquiredFrameNumber;
 }
 
-void BLASTBufferQueue::abandon() {
-    std::unique_lock _lock{mMutex};
-    // flush out the shadow queue
-    while (mNumFrameAvailable > 0) {
-        acquireAndReleaseBuffer();
-    }
-
-    // Clear submitted buffer states
-    mNumAcquired = 0;
-    mSubmitted.clear();
-    mPendingRelease.clear();
-
-    if (!mPendingTransactions.empty()) {
-        BQA_LOGD("Applying pending transactions on abandon %d",
-                 static_cast<uint32_t>(mPendingTransactions.size()));
-        SurfaceComposerClient::Transaction t;
-        mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
-        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
-        t.setApplyToken(mApplyToken).apply(false, true);
-    }
-
-    // Clear sync states
-    if (!mSyncedFrameNumbers.empty()) {
-        BQA_LOGD("mSyncedFrameNumbers cleared");
-        mSyncedFrameNumbers.clear();
-    }
-
-    if (mSyncTransaction != nullptr) {
-        BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s",
-                 mAcquireSingleBuffer ? "true" : "false");
-        mSyncTransaction = nullptr;
-        mAcquireSingleBuffer = false;
-    }
-
-    // abandon buffer queue
-    if (mBufferItemConsumer != nullptr) {
-        mBufferItemConsumer->abandon();
-        mBufferItemConsumer->setFrameAvailableListener(nullptr);
-    }
-    mBufferItemConsumer = nullptr;
-    mConsumer = nullptr;
-    mProducer = nullptr;
-}
-
 bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const {
     std::unique_lock _lock{mMutex};
     return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 95df811..4535c98 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -108,7 +108,6 @@
 
     uint32_t getLastTransformHint() const;
     uint64_t getLastAcquiredFrameNum();
-    void abandon();
 
     /**
      * Set a callback to be invoked when we are hung. The boolean parameter
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index c0aa2e2..5990ee0 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -314,7 +314,26 @@
     DEFINE_KEYCODE(REFRESH), \
     DEFINE_KEYCODE(THUMBS_UP), \
     DEFINE_KEYCODE(THUMBS_DOWN), \
-    DEFINE_KEYCODE(PROFILE_SWITCH)
+    DEFINE_KEYCODE(PROFILE_SWITCH), \
+    DEFINE_KEYCODE(VIDEO_APP_1), \
+    DEFINE_KEYCODE(VIDEO_APP_2), \
+    DEFINE_KEYCODE(VIDEO_APP_3), \
+    DEFINE_KEYCODE(VIDEO_APP_4), \
+    DEFINE_KEYCODE(VIDEO_APP_5), \
+    DEFINE_KEYCODE(VIDEO_APP_6), \
+    DEFINE_KEYCODE(VIDEO_APP_7), \
+    DEFINE_KEYCODE(VIDEO_APP_8), \
+    DEFINE_KEYCODE(FEATURED_APP_1), \
+    DEFINE_KEYCODE(FEATURED_APP_2), \
+    DEFINE_KEYCODE(FEATURED_APP_3), \
+    DEFINE_KEYCODE(FEATURED_APP_4), \
+    DEFINE_KEYCODE(DEMO_APP_1), \
+    DEFINE_KEYCODE(DEMO_APP_2), \
+    DEFINE_KEYCODE(DEMO_APP_3), \
+    DEFINE_KEYCODE(DEMO_APP_4), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE)
 
 // NOTE: If you add a new axis here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 662e568..16ffa10 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -75,7 +75,7 @@
     }
 
     char* end;
-    int value = strtol(stringValue.c_str(), &end, 10);
+    int32_t value = static_cast<int32_t>(strtol(stringValue.c_str(), &end, 10));
     if (*end != '\0') {
         ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.c_str(),
               stringValue.c_str());
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 6e991e9..e2bfb50 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -44,8 +44,8 @@
 
 void VelocityControl::reset() {
     mLastMovementTime = LLONG_MIN;
-    mRawPosition.x = 0;
-    mRawPosition.y = 0;
+    mRawPositionX = 0;
+    mRawPositionY = 0;
     mVelocityTracker.clear();
 }
 
@@ -61,17 +61,20 @@
 
         mLastMovementTime = eventTime;
         if (deltaX) {
-            mRawPosition.x += *deltaX;
+            mRawPositionX += *deltaX;
         }
         if (deltaY) {
-            mRawPosition.y += *deltaY;
+            mRawPositionY += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)),
+                                     {{AMOTION_EVENT_AXIS_X, {mRawPositionX}},
+                                      {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}});
 
-        float vx, vy;
+        std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
+        std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
         float scale = mParameters.scale;
-        if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
-            float speed = hypotf(vx, vy) * scale;
+        if (vx && vy) {
+            float speed = hypotf(*vx, *vy) * scale;
             if (speed >= mParameters.highThreshold) {
                 // Apply full acceleration above the high speed threshold.
                 scale *= mParameters.acceleration;
@@ -85,10 +88,9 @@
 
             if (DEBUG_ACCELERATION) {
                 ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
-                        "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
-                        mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
-                        mParameters.acceleration,
-                        vx, vy, speed, scale / mParameters.scale);
+                      "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
+                      mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
+                      mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale);
             }
 
         } else {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 76aaf61..6f88a38 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -125,29 +125,39 @@
 
 // --- VelocityTracker ---
 
+const std::set<int32_t> VelocityTracker::SUPPORTED_AXES = {AMOTION_EVENT_AXIS_X,
+                                                           AMOTION_EVENT_AXIS_Y};
+
+const std::set<int32_t> VelocityTracker::PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y};
+
 VelocityTracker::VelocityTracker(const Strategy strategy)
       : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
-    // Configure the strategy.
-    if (!configureStrategy(strategy)) {
-        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
-        if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
-            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
-                             "'!",
-                             strategy);
-        }
+    // Configure the strategy for each axis.
+    for (int32_t axis : SUPPORTED_AXES) {
+        configureStrategy(axis, strategy);
     }
 }
 
 VelocityTracker::~VelocityTracker() {
 }
 
-bool VelocityTracker::configureStrategy(Strategy strategy) {
+void VelocityTracker::configureStrategy(int32_t axis, const Strategy strategy) {
+    std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
+
     if (strategy == VelocityTracker::Strategy::DEFAULT) {
-        mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+        createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
     } else {
-        mStrategy = createStrategy(strategy);
+        createdStrategy = createStrategy(strategy);
     }
-    return mStrategy != nullptr;
+
+    if (createdStrategy == nullptr) {
+        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+        createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+        LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
+                            "Could not create the default velocity tracker strategy '%" PRId32 "'!",
+                            strategy);
+    }
+    mStrategies[axis] = std::move(createdStrategy);
 }
 
 std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
@@ -201,8 +211,9 @@
 void VelocityTracker::clear() {
     mCurrentPointerIdBits.clear();
     mActivePointerId = -1;
-
-    mStrategy->clear();
+    for (int32_t axis : SUPPORTED_AXES) {
+        mStrategies[axis]->clear();
+    }
 }
 
 void VelocityTracker::clearPointers(BitSet32 idBits) {
@@ -213,14 +224,13 @@
         mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
     }
 
-    mStrategy->clearPointers(idBits);
+    for (int32_t axis : SUPPORTED_AXES) {
+        mStrategies[axis]->clearPointers(idBits);
+    }
 }
 
 void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                  const std::vector<VelocityTracker::Position>& positions) {
-    LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
-                        "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
-                        idBits.count(), positions.size());
+                                  const std::map<int32_t /*axis*/, std::vector<float>>& positions) {
     while (idBits.count() > MAX_POINTERS) {
         idBits.clearLastMarkedBit();
     }
@@ -232,7 +242,9 @@
 
         // We have not received any movements for too long.  Assume that all pointers
         // have stopped.
-        mStrategy->clear();
+        for (const auto& [_, strategy] : mStrategies) {
+            strategy->clear();
+        }
     }
     mLastEventTime = eventTime;
 
@@ -241,29 +253,37 @@
         mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
     }
 
-    mStrategy->addMovement(eventTime, idBits, positions);
+    for (const auto& [axis, positionValues] : positions) {
+        LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(),
+                            "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+                            idBits.count(), positionValues.size());
+        mStrategies[axis]->addMovement(eventTime, idBits, positionValues);
+    }
 
     if (DEBUG_VELOCITY) {
         ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
               ", idBits=0x%08x, activePointerId=%d",
               eventTime, idBits.value, mActivePointerId);
-        for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
-            uint32_t id = iterBits.firstMarkedBit();
-            uint32_t index = idBits.getIndexOfBit(id);
-            iterBits.clearBit(id);
-            Estimator estimator;
-            getEstimator(id, &estimator);
-            ALOGD("  %d: position (%0.3f, %0.3f), "
-                  "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
-                  id, positions[index].x, positions[index].y, int(estimator.degree),
-                  vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
-                  vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
-                  estimator.confidence);
+        for (const auto& positionsEntry : positions) {
+            for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+                uint32_t id = iterBits.firstMarkedBit();
+                uint32_t index = idBits.getIndexOfBit(id);
+                iterBits.clearBit(id);
+                Estimator estimator;
+                getEstimator(positionsEntry.first, id, &estimator);
+                ALOGD("  %d: axis=%d, position=%0.3f, "
+                      "estimator (degree=%d, coeff=%s, confidence=%f)",
+                      id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree),
+                      vectorToString(estimator.coeff, estimator.degree + 1).c_str(),
+                      estimator.confidence);
+            }
         }
     }
 }
 
 void VelocityTracker::addMovement(const MotionEvent* event) {
+    // Stores data about which axes to process based on the incoming motion event.
+    std::set<int32_t> axesToProcess;
     int32_t actionMasked = event->getActionMasked();
 
     switch (actionMasked) {
@@ -271,6 +291,9 @@
     case AMOTION_EVENT_ACTION_HOVER_ENTER:
         // Clear all pointers on down before adding the new movement.
         clear();
+        for (int32_t axis : PLANAR_AXES) {
+            axesToProcess.insert(axis);
+        }
         break;
     case AMOTION_EVENT_ACTION_POINTER_DOWN: {
         // Start a new movement trace for a pointer that just went down.
@@ -279,10 +302,16 @@
         BitSet32 downIdBits;
         downIdBits.markBit(event->getPointerId(event->getActionIndex()));
         clearPointers(downIdBits);
+        for (int32_t axis : PLANAR_AXES) {
+            axesToProcess.insert(axis);
+        }
         break;
     }
     case AMOTION_EVENT_ACTION_MOVE:
     case AMOTION_EVENT_ACTION_HOVER_MOVE:
+        for (int32_t axis : PLANAR_AXES) {
+            axesToProcess.insert(axis);
+        }
         break;
     case AMOTION_EVENT_ACTION_POINTER_UP:
     case AMOTION_EVENT_ACTION_UP: {
@@ -293,7 +322,9 @@
                      toString(delaySinceLastEvent).c_str());
             // We have not received any movements for too long.  Assume that all pointers
             // have stopped.
-            mStrategy->clear();
+            for (int32_t axis : PLANAR_AXES) {
+                mStrategies[axis]->clear();
+            }
         }
         // These actions because they do not convey any new information about
         // pointer movement.  We also want to preserve the last known velocity of the pointers.
@@ -325,37 +356,56 @@
         pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
     }
 
-    std::vector<Position> positions;
-    positions.resize(pointerCount);
+    std::map<int32_t, std::vector<float>> positions;
+    for (int32_t axis : axesToProcess) {
+        positions[axis].resize(pointerCount);
+    }
 
     size_t historySize = event->getHistorySize();
     for (size_t h = 0; h <= historySize; h++) {
         nsecs_t eventTime = event->getHistoricalEventTime(h);
-        for (size_t i = 0; i < pointerCount; i++) {
-            uint32_t index = pointerIndex[i];
-            positions[index].x = event->getHistoricalX(i, h);
-            positions[index].y = event->getHistoricalY(i, h);
+        for (int32_t axis : axesToProcess) {
+            for (size_t i = 0; i < pointerCount; i++) {
+                positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h);
+            }
         }
         addMovement(eventTime, idBits, positions);
     }
 }
 
-bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
+std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const {
     Estimator estimator;
-    if (getEstimator(id, &estimator) && estimator.degree >= 1) {
-        *outVx = estimator.xCoeff[1];
-        *outVy = estimator.yCoeff[1];
-        return true;
+    bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1;
+    if (validVelocity) {
+        return estimator.coeff[1];
     }
-    *outVx = 0;
-    *outVy = 0;
-    return false;
+    return {};
 }
 
-bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
-    return mStrategy->getEstimator(id, outEstimator);
+VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t units,
+                                                                       float maxVelocity) {
+    ComputedVelocity computedVelocity;
+    for (int32_t axis : SUPPORTED_AXES) {
+        BitSet32 copyIdBits = BitSet32(mCurrentPointerIdBits);
+        while (!copyIdBits.isEmpty()) {
+            uint32_t id = copyIdBits.clearFirstMarkedBit();
+            std::optional<float> velocity = getVelocity(axis, id);
+            if (velocity) {
+                float adjustedVelocity =
+                        std::clamp(*velocity * units / 1000, -maxVelocity, maxVelocity);
+                computedVelocity.addVelocity(axis, id, adjustedVelocity);
+            }
+        }
+    }
+    return computedVelocity;
 }
 
+bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const {
+    if (SUPPORTED_AXES.find(axis) == SUPPORTED_AXES.end()) {
+        return false;
+    }
+    return mStrategies.at(axis)->getEstimator(id, outEstimator);
+}
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
@@ -378,9 +428,8 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                      const std::vector<float>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -627,8 +676,7 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    std::vector<float> x;
-    std::vector<float> y;
+    std::vector<float> positions;
     std::vector<float> w;
     std::vector<float> time;
 
@@ -645,15 +693,13 @@
             break;
         }
 
-        const VelocityTracker::Position& position = movement.getPosition(id);
-        x.push_back(position.x);
-        y.push_back(position.y);
+        positions.push_back(movement.getPosition(id));
         w.push_back(chooseWeight(index));
         time.push_back(-age * 0.000000001f);
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (x.size() < HISTORY_SIZE);
+    } while (positions.size() < HISTORY_SIZE);
 
-    const size_t m = x.size();
+    const size_t m = positions.size();
     if (m == 0) {
         return false; // no data
     }
@@ -666,39 +712,36 @@
 
     if (degree == 2 && mWeighting == WEIGHTING_NONE) {
         // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
-        std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
-        if (xCoeff && yCoeff) {
+        std::optional<std::array<float, 3>> coeff =
+                solveUnweightedLeastSquaresDeg2(time, positions);
+        if (coeff) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = 2;
             outEstimator->confidence = 1;
             for (size_t i = 0; i <= outEstimator->degree; i++) {
-                outEstimator->xCoeff[i] = (*xCoeff)[i];
-                outEstimator->yCoeff[i] = (*yCoeff)[i];
+                outEstimator->coeff[i] = (*coeff)[i];
             }
             return true;
         }
     } else if (degree >= 1) {
         // General case for an Nth degree polynomial fit
-        float xdet, ydet;
+        float det;
         uint32_t n = degree + 1;
-        if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
-            solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
+        if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = degree;
-            outEstimator->confidence = xdet * ydet;
+            outEstimator->confidence = det;
 
-            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
-                     int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
-                     vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
+                     int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(),
+                     outEstimator->confidence);
 
             return true;
         }
     }
 
     // No velocity data available for this pointer, but we do have its current position.
-    outEstimator->xCoeff[0] = x[0];
-    outEstimator->yCoeff[0] = y[0];
+    outEstimator->coeff[0] = positions[0];
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 0;
     outEstimator->confidence = 1;
@@ -790,18 +833,17 @@
     mPointerIdBits.value &= ~idBits.value;
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                     const std::vector<float>& positions) {
     uint32_t index = 0;
     for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
         uint32_t id = iterIdBits.clearFirstMarkedBit();
         State& state = mPointerState[id];
-        const VelocityTracker::Position& position = positions[index++];
+        const float position = positions[index++];
         if (mPointerIdBits.hasBit(id)) {
-            updateState(state, eventTime, position.x, position.y);
+            updateState(state, eventTime, position);
         } else {
-            initState(state, eventTime, position.x, position.y);
+            initState(state, eventTime, position);
         }
     }
 
@@ -821,21 +863,18 @@
     return false;
 }
 
-void IntegratingVelocityTrackerStrategy::initState(State& state,
-        nsecs_t eventTime, float xpos, float ypos) const {
+void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime,
+                                                   float pos) const {
     state.updateTime = eventTime;
     state.degree = 0;
 
-    state.xpos = xpos;
-    state.xvel = 0;
-    state.xaccel = 0;
-    state.ypos = ypos;
-    state.yvel = 0;
-    state.yaccel = 0;
+    state.pos = pos;
+    state.accel = 0;
+    state.vel = 0;
 }
 
-void IntegratingVelocityTrackerStrategy::updateState(State& state,
-        nsecs_t eventTime, float xpos, float ypos) const {
+void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime,
+                                                     float pos) const {
     const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
     const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
 
@@ -846,34 +885,26 @@
     float dt = (eventTime - state.updateTime) * 0.000000001f;
     state.updateTime = eventTime;
 
-    float xvel = (xpos - state.xpos) / dt;
-    float yvel = (ypos - state.ypos) / dt;
+    float vel = (pos - state.pos) / dt;
     if (state.degree == 0) {
-        state.xvel = xvel;
-        state.yvel = yvel;
+        state.vel = vel;
         state.degree = 1;
     } else {
         float alpha = dt / (FILTER_TIME_CONSTANT + dt);
         if (mDegree == 1) {
-            state.xvel += (xvel - state.xvel) * alpha;
-            state.yvel += (yvel - state.yvel) * alpha;
+            state.vel += (vel - state.vel) * alpha;
         } else {
-            float xaccel = (xvel - state.xvel) / dt;
-            float yaccel = (yvel - state.yvel) / dt;
+            float accel = (vel - state.vel) / dt;
             if (state.degree == 1) {
-                state.xaccel = xaccel;
-                state.yaccel = yaccel;
+                state.accel = accel;
                 state.degree = 2;
             } else {
-                state.xaccel += (xaccel - state.xaccel) * alpha;
-                state.yaccel += (yaccel - state.yaccel) * alpha;
+                state.accel += (accel - state.accel) * alpha;
             }
-            state.xvel += (state.xaccel * dt) * alpha;
-            state.yvel += (state.yaccel * dt) * alpha;
+            state.vel += (state.accel * dt) * alpha;
         }
     }
-    state.xpos = xpos;
-    state.ypos = ypos;
+    state.pos = pos;
 }
 
 void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
@@ -881,12 +912,9 @@
     outEstimator->time = state.updateTime;
     outEstimator->confidence = 1.0f;
     outEstimator->degree = state.degree;
-    outEstimator->xCoeff[0] = state.xpos;
-    outEstimator->xCoeff[1] = state.xvel;
-    outEstimator->xCoeff[2] = state.xaccel / 2;
-    outEstimator->yCoeff[0] = state.ypos;
-    outEstimator->yCoeff[1] = state.yvel;
-    outEstimator->yCoeff[2] = state.yaccel / 2;
+    outEstimator->coeff[0] = state.pos;
+    outEstimator->coeff[1] = state.vel;
+    outEstimator->coeff[2] = state.accel / 2;
 }
 
 
@@ -909,9 +937,8 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                const std::vector<float>& positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
@@ -959,12 +986,11 @@
     // overestimate the velocity at that time point.  Most samples might be measured
     // 16ms apart but some consecutive samples could be only 0.5sm apart because
     // the hardware or driver reports them irregularly or in bursts.
-    float accumVx = 0;
-    float accumVy = 0;
+    float accumV = 0;
     uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
     const Movement& oldestMovement = mMovements[oldestIndex];
-    const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
+    float oldestPosition = oldestMovement.getPosition(id);
     nsecs_t lastDuration = 0;
 
     while (numTouches-- > 1) {
@@ -978,26 +1004,22 @@
         // the velocity.  Consequently, we impose a minimum duration constraint on the
         // samples that we include in the calculation.
         if (duration >= MIN_DURATION) {
-            const VelocityTracker::Position& position = movement.getPosition(id);
+            float position = movement.getPosition(id);
             float scale = 1000000000.0f / duration; // one over time delta in seconds
-            float vx = (position.x - oldestPosition.x) * scale;
-            float vy = (position.y - oldestPosition.y) * scale;
-            accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
-            accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
+            float v = (position - oldestPosition) * scale;
+            accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration);
             lastDuration = duration;
             samplesUsed += 1;
         }
     }
 
     // Report velocity.
-    const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
+    float newestPosition = newestMovement.getPosition(id);
     outEstimator->time = newestMovement.eventTime;
     outEstimator->confidence = 1;
-    outEstimator->xCoeff[0] = newestPosition.x;
-    outEstimator->yCoeff[0] = newestPosition.y;
+    outEstimator->coeff[0] = newestPosition;
     if (samplesUsed) {
-        outEstimator->xCoeff[1] = accumVx;
-        outEstimator->yCoeff[1] = accumVy;
+        outEstimator->coeff[1] = accumV;
         outEstimator->degree = 1;
     } else {
         outEstimator->degree = 0;
@@ -1024,9 +1046,8 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                 const std::vector<float>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -1163,8 +1184,7 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    float x[HISTORY_SIZE];
-    float y[HISTORY_SIZE];
+    float positions[HISTORY_SIZE];
     nsecs_t time[HISTORY_SIZE];
     size_t m = 0; // number of points that will be used for fitting
     size_t index = mIndex;
@@ -1180,9 +1200,7 @@
             break;
         }
 
-        const VelocityTracker::Position& position = movement.getPosition(id);
-        x[m] = position.x;
-        y[m] = position.y;
+        positions[m] = movement.getPosition(id);
         time[m] = movement.eventTime;
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
     } while (++m < HISTORY_SIZE);
@@ -1190,33 +1208,30 @@
     if (m == 0) {
         return false; // no data
     }
-    outEstimator->xCoeff[0] = 0;
-    outEstimator->yCoeff[0] = 0;
-    outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m);
-    outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m);
-    outEstimator->xCoeff[2] = 0;
-    outEstimator->yCoeff[2] = 0;
+    outEstimator->coeff[0] = 0;
+    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m);
+    outEstimator->coeff[2] = 0;
+
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 2; // similar results to 2nd degree fit
     outEstimator->confidence = 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: (%.1f, %.1f)", outEstimator->xCoeff[1],
-             outEstimator->yCoeff[1]);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
-        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons
+        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
+        // X axis chosen arbitrarily for velocity comparisons.
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
         BitSet32 idBits;
         const uint32_t pointerId = 0;
         idBits.markBit(pointerId);
         for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], idBits, {{x[i], y[i]}});
+            lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}});
         }
-        float outVx = 0, outVy = 0;
-        const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy);
-        if (computed) {
-            ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy);
+        std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
+        if (v) {
+            ALOGD("lsq2 velocity: %.1f", *v);
         } else {
             ALOGD("lsq2 velocity: could not compute velocity");
         }
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 4a445de..f8e5052 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "VelocityTracker_test"
 
+#include <math.h>
 #include <array>
 #include <chrono>
-#include <math.h>
+#include <limits>
 
 #include <android-base/stringprintf.h>
 #include <attestation/HmacKeyManager.h>
@@ -198,25 +199,13 @@
                                     const std::vector<MotionEventEntry>& motions, int32_t axis,
                                     float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) {
     VelocityTracker vt(strategy);
-    float Vx, Vy;
 
     std::vector<MotionEvent> events = createMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
 
-    vt.getVelocity(pointerId, &Vx, &Vy);
-
-    switch (axis) {
-    case AMOTION_EVENT_AXIS_X:
-        checkVelocity(Vx, targetVelocity);
-        break;
-    case AMOTION_EVENT_AXIS_Y:
-        checkVelocity(Vy, targetVelocity);
-        break;
-    default:
-        FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
-    }
+    checkVelocity(vt.getVelocity(axis, pointerId).value_or(0), targetVelocity);
 }
 
 static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
@@ -226,17 +215,99 @@
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    VelocityTracker::Estimator estimator;
-    EXPECT_TRUE(vt.getEstimator(0, &estimator));
+    VelocityTracker::Estimator estimatorX;
+    VelocityTracker::Estimator estimatorY;
+    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX));
+    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY));
     for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient(estimator.xCoeff[i], coefficients[i]);
-        checkCoefficient(estimator.yCoeff[i], coefficients[i]);
+        checkCoefficient(estimatorX.coeff[i], coefficients[i]);
+        checkCoefficient(estimatorY.coeff[i], coefficients[i]);
     }
 }
 
 /*
  * ================== VelocityTracker tests generated manually =====================================
  */
+TEST_F(VelocityTrackerTest, TestComputedVelocity) {
+    VelocityTracker::ComputedVelocity computedVelocity;
+
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/);
+
+    // Check the axes/indices with velocity.
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000);
+    for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
+        // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id.
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, id))
+                << "Empty scroll data expected at id=" << id;
+        if (id == 0 || id == 26U || id == 27U || id == MAX_POINTER_ID) {
+            // Already checked above; continue.
+            continue;
+        }
+        // No data was added to X/Y for this id, expect empty value.
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id))
+                << "Empty X data expected at id=" << id;
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id))
+                << "Empty Y data expected at id=" << id;
+    }
+    // Out-of-bounds ids should given empty values.
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, -1));
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1));
+}
+
+TEST_F(VelocityTrackerTest, TestGetComputedVelocity) {
+    std::vector<MotionEventEntry> motions = {
+            {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}},
+            {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}},
+            {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}},
+            {235089112497111ns, {{528.25, 0}}}, {235089118760000ns, {{531.00, 0}}},
+            {235089126686000ns, {{535.00, 0}}}, {235089129316820ns, {{536.33, 0}}},
+            {235089135199000ns, {{540.00, 0}}}, {235089144297000ns, {{546.00, 0}}},
+            {235089146136443ns, {{547.21, 0}}}, {235089152923000ns, {{553.00, 0}}},
+            {235089160784000ns, {{559.00, 0}}}, {235089162955851ns, {{560.66, 0}}},
+            {235089162955851ns, {{560.66, 0}}}, // ACTION_UP
+    };
+    VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
+    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    for (const MotionEvent& event : events) {
+        vt.addMovement(&event);
+    }
+
+    float maxFloat = std::numeric_limits<float>::max();
+    VelocityTracker::ComputedVelocity computedVelocity;
+    computedVelocity = vt.getComputedVelocity(1000 /* units */, maxFloat);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
+                  764.345703);
+
+    // Expect X velocity to be scaled with respective to provided units.
+    computedVelocity = vt.getComputedVelocity(1000000 /* units */, maxFloat);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
+                  764345.703);
+
+    // Expect X velocity to be clamped by provided max velocity.
+    computedVelocity = vt.getComputedVelocity(1000000 /* units */, 1000);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000);
+
+    // All 0 data for Y; expect 0 velocity.
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)), 0);
+
+    // No data for scroll-axis; expect empty velocity.
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID));
+}
+
 TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) {
     // Same coordinate is reported 2 times in a row
     // It is difficult to determine the correct answer here, but at least the direction
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index a6cacad..78f692b 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -22,12 +22,12 @@
 #include <cutils/native_handle.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/Vector.h>
 #include <utils/Timers.h>
+#include <utils/Vector.h>
 
-#include <binder/Parcel.h>
 #include <binder/IInterface.h>
 #include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
 
 #include <sensor/Sensor.h>
 #include <sensor/ISensorEventConnection.h>
@@ -205,9 +205,10 @@
             if (resource == nullptr) {
                 return BAD_VALUE;
             }
+            native_handle_set_fdsan_tag(resource);
             sp<ISensorEventConnection> ch =
                     createSensorDirectConnection(opPackageName, size, type, format, resource);
-            native_handle_close(resource);
+            native_handle_close_with_tag(resource);
             native_handle_delete(resource);
             reply->writeStrongBinder(IInterface::asBinder(ch));
             return NO_ERROR;
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 9120972..d0c03fe 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,9 +17,10 @@
 #pragma once
 
 #include <cstdint>
-#include <optional>
 #include <string>
 
+#include <ftl/optional.h>
+
 namespace android {
 
 // ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
@@ -68,7 +69,7 @@
 
 // DisplayId of a physical display, such as the internal display or externally connected display.
 struct PhysicalDisplayId : DisplayId {
-    static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+    static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
         if (id.value & FLAG_VIRTUAL) {
             return std::nullopt;
         }
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index 566e417..83da821 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -23,7 +23,7 @@
 
 namespace android::ui {
 
-enum class DisplayConnectionType { Internal, External };
+enum class DisplayConnectionType { Internal, External, ftl_last = External };
 
 // Immutable information about physical display.
 struct StaticDisplayInfo {
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 8300e8a..fe37287 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_BLOCKING_QUEUE_H
-#define _UI_INPUT_BLOCKING_QUEUE_H
+#pragma once
 
 #include "android-base/thread_annotations.h"
 #include <condition_variable>
@@ -106,6 +105,4 @@
     std::vector<T> mQueue GUARDED_BY(mLock);
 };
 
-
 } // namespace android
-#endif
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index b40ab7f..1137193 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_MANAGER_H
-#define _UI_INPUT_MANAGER_H
+#pragma once
 
 /**
  * Native input manager.
@@ -130,5 +129,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_MANAGER_H
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index 261e012..bbf391e 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_CLASSIFIER_H
-#define _UI_INPUT_CLASSIFIER_H
+#pragma once
 
 #include <android-base/thread_annotations.h>
 #include <future>
@@ -288,4 +287,3 @@
 };
 
 } // namespace android
-#endif
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
index 097dba5..5e5e0c4 100644
--- a/services/inputflinger/dispatcher/AnrTracker.h
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
-#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <utils/Timers.h>
@@ -56,5 +55,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index fec5f83..d210e9e 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
-#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+#pragma once
 
 #include <utils/BitSet.h>
 #include <optional>
@@ -55,5 +54,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index dc6a081..6040e9b 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
-#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+#pragma once
 
 #include "InputState.h"
 
@@ -74,5 +73,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 4f8995f..d2ad407 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
-#define _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
+#pragma once
 
 #define LOG_TAG "InputDispatcher"
 
@@ -75,11 +74,8 @@
 
 /**
  * Log debug messages about touch occlusion
- * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
- * restart)
  */
-const bool DEBUG_TOUCH_OCCLUSION =
-        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
 
 /**
  * Log debug messages about the app switch latency optimization.
@@ -95,5 +91,3 @@
 const bool DEBUG_HOVER =
         __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index d1c8b8a..9809148 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+#pragma once
 
 #include <gui/WindowInfo.h>
 #include <utils/StrongPointer.h>
@@ -44,5 +43,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 0f79296..1f916f3 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H
-#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+#pragma once
 
 #include "InjectionState.h"
 #include "InputTarget.h"
@@ -257,5 +256,3 @@
                                                        const ui::Transform& rawTransform);
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 90cf150..d9e27ba 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#pragma once
 
 #include <stdint.h>
 #include "InputDispatcherInterface.h"
@@ -41,5 +40,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index be619ae..b5bbce8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_DISPATCHER_H
-#define _UI_INPUT_DISPATCHER_H
+#pragma once
 
 #include "AnrTracker.h"
 #include "CancelationOptions.h"
@@ -693,5 +692,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index 77b8472..daf375d 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <input/Input.h>
@@ -104,5 +103,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 77a6db1..6ab48c9 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+#pragma once
 
 #include "CancelationOptions.h"
 #include "Entry.h"
@@ -134,5 +133,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index ac20dab..b2966f6 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+#pragma once
 
 #include <gui/constants.h>
 #include <input/InputTransport.h>
@@ -136,5 +135,3 @@
 std::string dispatchModeToString(int32_t dispatchMode);
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
index ed5731f..accfc29 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.h
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
-#define _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
+#pragma once
 
 #include <kll.h>
 #include <statslog.h>
@@ -86,5 +85,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 4b0c618..64dfeef 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
-#define _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
+#pragma once
 
 #include <map>
 #include <unordered_map>
@@ -81,5 +80,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 365d5be..7b51191 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H
-#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+#pragma once
 
 #include <input/InputTransport.h>
 
@@ -30,5 +29,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 1fb51e1..cf5f1e5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+#pragma once
 
 #include "Monitor.h"
 #include "TouchedWindow.h"
@@ -67,5 +66,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index a7839db..a6c505b 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
-#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+#pragma once
 
 namespace android {
 
@@ -38,5 +37,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
index 00abf47..5eb3a32 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+#pragma once
 
 #include <utils/Timers.h>
 
@@ -40,5 +39,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index 38d0c32..5247d8e 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+#pragma once
 
 #include <utils/StrongPointer.h>
 
@@ -29,5 +28,3 @@
         const sp<InputDispatcherPolicyInterface>& policy);
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 32b3ddb..484b0d3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+#pragma once
 
 #include <InputListener.h>
 #include <android-base/result.h>
@@ -226,5 +225,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 7c299b2..44f3baf 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+#pragma once
 
 #include "InputDispatcherConfiguration.h"
 
@@ -143,5 +142,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
diff --git a/services/inputflinger/host/InputDriver.h b/services/inputflinger/host/InputDriver.h
index e56673b..b4c9094 100644
--- a/services/inputflinger/host/InputDriver.h
+++ b/services/inputflinger/host/InputDriver.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_DRIVER_H
-#define ANDROID_INPUT_DRIVER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -192,4 +191,3 @@
 }
 
 } // namespace android
-#endif // ANDROID_INPUT_DRIVER_H
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 3cf1b2b..388988b 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_FLINGER_H
-#define ANDROID_INPUT_FLINGER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -58,5 +57,3 @@
 };
 
 } // namespace android
-
-#endif // ANDROID_INPUT_FLINGER_H
diff --git a/services/inputflinger/host/InputHost.h b/services/inputflinger/host/InputHost.h
index eda4a89..bdc4225 100644
--- a/services/inputflinger/host/InputHost.h
+++ b/services/inputflinger/host/InputHost.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_HOST_H
-#define ANDROID_INPUT_HOST_H
+#pragma once
 
 #include <vector>
 
@@ -55,4 +54,3 @@
 };
 
 } // namespace android
-#endif // ANDRIOD_INPUT_HOST_H
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index d9822ce..c8ab6c5 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_LISTENER_H
-#define _UI_INPUT_LISTENER_H
+#pragma once
 
 #include <vector>
 
@@ -292,5 +291,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_LISTENER_H
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 77c9142..cacb63c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_READER_BASE_H
-#define _UI_INPUT_READER_BASE_H
+#pragma once
 
 #include <android/os/IInputConstants.h>
 #include <input/DisplayViewport.h>
@@ -114,6 +113,8 @@
     virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
     /* Get battery status of a particular input device. */
     virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
+    /* Get the device path for the battery of an input device. */
+    virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
 
     virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
 
@@ -395,5 +396,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_READER_COMMON_H
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index 407365a..5e75027 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_THREAD_H
-#define _UI_INPUT_THREAD_H
+#pragma once
 
 #include <utils/Thread.h>
 
@@ -42,5 +41,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index db4228d..647e10c 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
-#define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
+#pragma once
 
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
@@ -108,5 +107,3 @@
 };
 
 } // namespace android
-
-#endif // _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
index 736041e..04c2e39 100644
--- a/services/inputflinger/include/VibrationElement.h
+++ b/services/inputflinger/include/VibrationElement.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _VIBRATION_ELEMENT_H
-#define _VIBRATION_ELEMENT_H
+#pragma once
 
 #include <array>
 #include <chrono>
@@ -71,5 +70,3 @@
 };
 
 } // namespace android
-
-#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index ff0b9a5..c9d21dc 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1555,7 +1555,7 @@
         // the lock to prevent event processing from being blocked by this call.
         std::scoped_lock _l(mLock);
 
-        const auto infos = getBatteryInfoLocked(deviceId);
+        const auto& infos = getBatteryInfoLocked(deviceId);
         auto it = infos.find(batteryId);
         if (it == infos.end()) {
             return std::nullopt;
@@ -1596,7 +1596,7 @@
         // the lock to prevent event processing from being blocked by this call.
         std::scoped_lock _l(mLock);
 
-        const auto infos = getBatteryInfoLocked(deviceId);
+        const auto& infos = getBatteryInfoLocked(deviceId);
         auto it = infos.find(batteryId);
         if (it == infos.end()) {
             return std::nullopt;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 4750d90..4c38ce8 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -721,7 +721,10 @@
 
     if (!eventHubId) return {};
     const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
-    if (batteryIds.empty()) return {};
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
+    }
     return mEventHub->getBatteryCapacity(*eventHubId, batteryIds.front());
 }
 
@@ -741,10 +744,35 @@
 
     if (!eventHubId) return {};
     const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
-    if (batteryIds.empty()) return {};
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
+    }
     return mEventHub->getBatteryStatus(*eventHubId, batteryIds.front());
 }
 
+std::optional<std::string> InputReader::getBatteryDevicePath(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (!device) return {};
+
+    std::optional<int32_t> eventHubId = device->getBatteryEventHubId();
+    if (!eventHubId) return {};
+    const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
+    }
+    const auto batteryInfo = mEventHub->getRawBatteryInfo(*eventHubId, batteryIds.front());
+    if (!batteryInfo) {
+        ALOGW("%s: Failed to get RawBatteryInfo for battery %d of EventHub device %d", __func__,
+              batteryIds.front(), *eventHubId);
+        return {};
+    }
+    return batteryInfo->path;
+}
+
 std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 1bbf386..e107d88 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_MACROS_H
-#define _UI_INPUTREADER_MACROS_H
+#pragma once
 
 #define LOG_TAG "InputReader"
 
@@ -115,5 +114,3 @@
 }
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index ac951eb..25cf435 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
-#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#pragma once
 
 #include "PeripheralControllerInterface.h"
 
@@ -150,5 +149,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
index 306e361..76ed1ca 100644
--- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H
-#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+#pragma once
 
 #include "EventHub.h"
 #include "InputDevice.h"
@@ -50,5 +49,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index dfb98f1..5cf2214 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _RUNTIME_EVENT_HUB_H
-#define _RUNTIME_EVENT_HUB_H
+#pragma once
 
 #include <bitset>
 #include <climits>
@@ -732,5 +731,3 @@
 };
 
 } // namespace android
-
-#endif // _RUNTIME_EVENT_HUB_H
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 3ef1840..4ae9ae9 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_DEVICE_H
-#define _UI_INPUTREADER_INPUT_DEVICE_H
+#pragma once
 
 #include <ftl/flags.h>
 #include <input/DisplayViewport.h>
@@ -409,5 +408,3 @@
 };
 
 } // namespace android
-
-#endif //_UI_INPUTREADER_INPUT_DEVICE_H
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index ae41e01..fbce87f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_READER_H
-#define _UI_INPUTREADER_INPUT_READER_H
+#pragma once
 
 #include <PointerControllerInterface.h>
 #include <android-base/thread_annotations.h>
@@ -100,6 +99,8 @@
 
     std::optional<int32_t> getBatteryStatus(int32_t deviceId) override;
 
+    std::optional<std::string> getBatteryDevicePath(int32_t deviceId) override;
+
     std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) override;
 
     std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override;
@@ -246,5 +247,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_READER_H
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 823d160..f2f156c 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H
-#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+#pragma once
 
 #include <input/InputDevice.h>
 
@@ -65,5 +64,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
index 17f158c..8d14d3c 100644
--- a/services/inputflinger/reader/include/StylusState.h
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_STYLUS_STATE_H
-#define _UI_INPUTREADER_STYLUS_STATE_H
+#pragma once
 
 #include <input/Input.h>
 
@@ -49,5 +48,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_STYLUS_STATE_H
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 7de9b830..08eba31 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
-#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#pragma once
 
 #include <android-base/unique_fd.h>
 #include <input/TouchVideoFrame.h>
@@ -123,5 +122,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 6058a1e..d6d324b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -25,6 +25,8 @@
 #include "PointerControllerInterface.h"
 #include "TouchCursorInputMapperCommon.h"
 
+#include "input/PrintTools.h"
+
 namespace android {
 
 // The default velocity control parameters that has no effect.
@@ -117,6 +119,7 @@
                          toString(mCursorScrollAccumulator.haveRelativeHWheel()));
     dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
     dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+    dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
     dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
     dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
     dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -205,21 +208,34 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
         configurePointerCapture) {
+        const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+        mDisplayId = ADISPLAY_ID_NONE;
+        if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+            // This InputDevice is associated with a viewport.
+            // Only generate events for the associated display.
+            const bool mismatchedPointerDisplay =
+                    isPointer && (viewport->displayId != mPointerController->getDisplayId());
+            mDisplayId = mismatchedPointerDisplay ? std::nullopt
+                                                  : std::make_optional(viewport->displayId);
+        } else if (isPointer) {
+            // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+            mDisplayId = mPointerController->getDisplayId();
+        }
+
         mOrientation = DISPLAY_ORIENTATION_0;
         const bool isOrientedDevice =
                 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-
         // 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
         // orientation-aware, then we need to apply the inverse rotation of the display so that
         // when the display rotation is applied later as a part of the per-window transform, we
         // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
         // rotations and report values directly from the input device.
-        if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
-            std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::INTERNAL);
-            if (internalViewport) {
-                mOrientation = getInverseRotation(internalViewport->orientation);
+        if (!isOrientedDevice && mDisplayId &&
+            mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+            if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+                mOrientation = getInverseRotation(viewport->orientation);
             }
         }
 
@@ -282,6 +298,11 @@
 }
 
 void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    if (!mDisplayId) {
+        // Ignore events when there is no target display configured.
+        return;
+    }
+
     int32_t lastButtonState = mButtonState;
     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
     mButtonState = currentButtonState;
@@ -327,7 +348,6 @@
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-    int32_t displayId = ADISPLAY_ID_NONE;
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -351,7 +371,6 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = mPointerController->getDisplayId();
     } else {
         // Pointer capture and navigation modes
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -373,7 +392,7 @@
 
     // Synthesize key down from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, displayId, policyFlags, lastButtonState, currentButtonState);
+                         mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -394,7 +413,7 @@
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
                 NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
-                                             getDeviceId(), mSource, displayId, policyFlags,
+                                             getDeviceId(), mSource, *mDisplayId, policyFlags,
                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                              metaState, buttonState, MotionClassification::NONE,
                                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -406,7 +425,7 @@
         }
 
         NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
+                              *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
                               currentButtonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                               mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
@@ -419,7 +438,7 @@
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
                 NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                           mSource, displayId, policyFlags,
+                                           mSource, *mDisplayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -435,7 +454,7 @@
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
             NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                       mSource, displayId, policyFlags,
+                                       mSource, *mDisplayId, policyFlags,
                                        AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                                        currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -450,7 +469,7 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
             NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                        mSource, displayId, policyFlags,
+                                        mSource, *mDisplayId, policyFlags,
                                         AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                                         currentButtonState, MotionClassification::NONE,
                                         AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -462,7 +481,7 @@
 
     // Synthesize key up from buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         displayId, policyFlags, lastButtonState, currentButtonState);
+                         *mDisplayId, policyFlags, lastButtonState, currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
     mCursorScrollAccumulator.finishSync();
@@ -477,16 +496,7 @@
 }
 
 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
-    if (mParameters.hasAssociatedDisplay) {
-        if (mParameters.mode == Parameters::Mode::POINTER) {
-            return std::make_optional(mPointerController->getDisplayId());
-        } else {
-            // If the device is orientationAware and not a mouse,
-            // it expects to dispatch events to any display
-            return std::make_optional(ADISPLAY_ID_NONE);
-        }
-    }
-    return std::nullopt;
+    return mDisplayId;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 75aeffb..a0229a7 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+#pragma once
 
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
@@ -111,6 +110,10 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // The display that events generated by this mapper should target. This can be set to
+    // 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;
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
@@ -125,5 +128,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 516aa51..03d9909 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
-#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -49,5 +48,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 7858728..5567cac 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_MAPPER_H
-#define _UI_INPUTREADER_INPUT_MAPPER_H
+#pragma once
 
 #include "EventHub.h"
 #include "InputDevice.h"
@@ -109,5 +108,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 307bf5b..e002397 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
-#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -111,5 +110,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 31251f4..2136d25 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
-#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -102,5 +101,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index fe8af5d..212c9c9 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include "TouchInputMapper.h"
 
@@ -122,5 +121,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 1859355..42e2421 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
-#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+#pragma once
 
 #include "CursorScrollAccumulator.h"
 #include "InputMapper.h"
@@ -46,5 +45,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 27a6177..38d4c3c 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -133,5 +132,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 9cb3f67..f54c195 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include "SingleTouchMotionAccumulator.h"
 #include "TouchInputMapper.h"
@@ -40,5 +39,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 64b9aa2..e0c949f 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -41,5 +40,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 31a3d2e..42d819b 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
-#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#pragma once
 
 #include <input/DisplayViewport.h>
 #include <stdint.h>
@@ -99,5 +98,3 @@
 }
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 539e24a..8c241f2 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2712,17 +2712,18 @@
 
     // Update the velocity tracker.
     {
-        std::vector<VelocityTracker::Position> positions;
+        std::vector<float> positionsX;
+        std::vector<float> positionsY;
         for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            float x = pointer.x * mPointerXMovementScale;
-            float y = pointer.y * mPointerYMovementScale;
-            positions.push_back({x, y});
+            positionsX.push_back(pointer.x * mPointerXMovementScale);
+            positionsY.push_back(pointer.y * mPointerYMovementScale);
         }
         mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
-                                                    positions);
+                                                    {{AMOTION_EVENT_AXIS_X, positionsX},
+                                                     {AMOTION_EVENT_AXIS_Y, positionsY}});
     }
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
@@ -2829,9 +2830,12 @@
             float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
             for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
                 uint32_t id = idBits.clearFirstMarkedBit();
-                float vx, vy;
-                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
-                    float speed = hypotf(vx, vy);
+                std::optional<float> vx =
+                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
+                std::optional<float> vy =
+                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
+                if (vx && vy) {
+                    float speed = hypotf(*vx, *vy);
                     if (speed > bestSpeed) {
                         bestId = id;
                         bestSpeed = speed;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index fe17006..bd4cff6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include <stdint.h>
 
@@ -805,5 +804,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index d3c22b6..894c573 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -50,5 +49,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 9e15906..ed4c789 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
-#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -48,5 +47,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index 1649559..ae1b7a3 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
-#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
index 4c011f1..93056f0 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
-#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -53,5 +52,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 22ebb72..7b889be 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
-#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -62,5 +61,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reporter/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h
index e5d3606..72a4aeb 100644
--- a/services/inputflinger/reporter/InputReporterInterface.h
+++ b/services/inputflinger/reporter/InputReporterInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_REPORTER_INTERFACE_H
-#define _UI_INPUT_REPORTER_INTERFACE_H
+#pragma once
 
 #include <utils/RefBase.h>
 
@@ -49,5 +48,3 @@
 sp<InputReporterInterface> createInputReporter();
 
 } // namespace android
-
-#endif // _UI_INPUT_REPORTER_INTERFACE_H
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 851043d..ee6993c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -31,6 +31,7 @@
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
 #include <TestInputListener.h>
+#include <TestInputListenerMatchers.h>
 #include <TouchInputMapper.h>
 #include <UinputDevice.h>
 #include <VibratorInputMapper.h>
@@ -47,7 +48,7 @@
 namespace android {
 
 using namespace ftl::flag_operators;
-
+using testing::AllOf;
 using std::chrono_literals::operator""ms;
 
 // Timeout for waiting for an expected event
@@ -59,7 +60,9 @@
 
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
+static const std::string DISPLAY_UNIQUE_ID = "local:1";
 static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
 static constexpr int32_t DISPLAY_WIDTH = 480;
 static constexpr int32_t DISPLAY_HEIGHT = 800;
 static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
@@ -78,6 +81,7 @@
 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;
@@ -1014,7 +1018,12 @@
 
     std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
                                                     int32_t batteryId) const override {
-        return std::nullopt;
+        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 {
@@ -2232,6 +2241,21 @@
     ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
 }
 
+TEST_F(InputReaderTest, BatteryGetDevicePath) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    ftl::Flags<InputDeviceClass> deviceClass =
+            InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    device->addController<FakePeripheralController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), BATTERY_DEVPATH);
+}
+
 TEST_F(InputReaderTest, LightGetColor) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     ftl::Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
@@ -2909,7 +2933,6 @@
 
     // Device should be disabled because it is associated with a specific display, but the
     // corresponding display is not found.
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                        InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2940,7 +2963,6 @@
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
@@ -4255,10 +4277,14 @@
                             int32_t rotatedX, int32_t rotatedY);
 
     void prepareDisplay(int32_t orientation) {
-        const std::string uniqueId = "local:0";
-        const ViewportType viewportType = ViewportType::INTERNAL;
-        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                orientation, uniqueId, NO_PORT, viewportType);
+        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,
+                                     ViewportType::EXTERNAL);
     }
 
     static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y,
@@ -4535,6 +4561,7 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
     // need to be rotated.
@@ -4553,11 +4580,13 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
@@ -4568,6 +4597,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
@@ -4578,6 +4608,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
@@ -4588,6 +4619,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
@@ -5084,33 +5116,78 @@
     ASSERT_EQ(20, args.pointerCoords[0].getY());
 }
 
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    // Setup for second display.
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-    const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
-    mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
-                                    true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
-                                    ViewportType::EXTERNAL);
-    mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown.
+    // The InputDevice is not associated with any display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
-    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     mFakePointerController->setPosition(100, 200);
     mFakePointerController->setButtonState(0);
 
-    NotifyMotionArgs args;
+    // Ensure input events are generated for the secondary display.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
-    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(110.0f, 220.0f))));
     ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
-    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown,
+    // and associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(110.0f, 220.0f))));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display as the display on which the pointer should be shown.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+    // Associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // The mapper should not generate any events because it is associated with a display that is
+    // different from the pointer display.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 // --- TouchInputMapperTest ---
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0bdfc6b..cad698f 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_TEST_INPUT_LISTENER_H
-#define _UI_TEST_INPUT_LISTENER_H
+#pragma once
 
 #include <android-base/thread_annotations.h>
 #include <gmock/gmock.h>
@@ -103,4 +102,3 @@
 };
 
 } // namespace android
-#endif
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
new file mode 100644
index 0000000..03736a3
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/input.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+MATCHER_P(WithMotionAction, action, "InputEvent with specified action") {
+    if (action == AMOTION_EVENT_ACTION_CANCEL) {
+        *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+        return (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+    }
+    *result_listener << "expected action " << MotionEvent::actionToString(action) << ", but got "
+                     << MotionEvent::actionToString(arg.action);
+    return action == arg.action;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+    *result_listener << "expected source " << source << ", but got " << arg.source;
+    return arg.source == source;
+}
+
+MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
+    *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId;
+    return arg.displayId == displayId;
+}
+
+MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
+    const auto argX = arg.pointerCoords[0].getX();
+    const auto argY = arg.pointerCoords[0].getY();
+    *result_listener << "expected coords (" << x << ", " << y << "), but got (" << argX << ", "
+                     << argY << ")";
+    return argX == x && argY == y;
+}
+
+MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
+    *result_listener << "expected flags " << flags << ", but got " << arg.flags;
+    return arg.flags == flags;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index a37fc2b..e0ff8c3 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H
-#define _UI_TEST_INPUT_UINPUT_INJECTOR_H
+#pragma once
 
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
@@ -155,5 +154,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 526e4eb..4c84160 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -24,6 +24,7 @@
 #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 
 #include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
 
 using ::testing::AllOf;
 
@@ -55,21 +56,6 @@
 
 constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
 
-MATCHER_P(WithAction, action, "MotionEvent with specified action") {
-    bool result = true;
-    if (action == CANCEL) {
-        result &= (arg.flags & FLAG_CANCELED) != 0;
-    }
-    result &= arg.action == action;
-    *result_listener << "expected to receive " << MotionEvent::actionToString(action)
-                     << " but received " << MotionEvent::actionToString(arg.action) << " instead.";
-    return result;
-}
-
-MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") {
-    return arg.flags == flags;
-}
-
 static nsecs_t toNs(std::chrono::nanoseconds duration) {
     return duration.count();
 }
@@ -605,19 +591,19 @@
     // Small touch down
     NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
     mBlocker->notifyMotion(&args1);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Large touch oval on the next move
     NotifyMotionArgs args2 =
             generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
     mBlocker->notifyMotion(&args2);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the touch to force the model to decide on whether it's a palm
     NotifyMotionArgs args3 =
             generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
     mBlocker->notifyMotion(&args3);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(CANCEL));
 }
 
 /**
@@ -633,14 +619,14 @@
     NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
     args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args1);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
     NotifyMotionArgs args2 =
             generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
     args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args2);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
     // it's a palm.
@@ -648,7 +634,7 @@
             generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
     args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args3);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
 }
 
 /**
@@ -664,21 +650,21 @@
     // Touch down
     NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
     mBlocker->notifyMotion(&args1);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
 
     // Stylus pointer down
     NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN,
                                                 {{1, 2, 3}, {10, 20, 30}});
     args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args2);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(POINTER_1_DOWN));
 
     // Large touch oval on the next finger move
     NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE,
                                                 {{1, 2, 300}, {11, 21, 30}});
     args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args3);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the finger pointer. It should be canceled due to the heuristic filter.
     NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
@@ -686,14 +672,14 @@
     args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args4);
     mTestListener.assertNotifyMotionWasCalled(
-            AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
+            AllOf(WithMotionAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
 
     NotifyMotionArgs args5 =
             generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
     args5.pointerProperties[0].id = args4.pointerProperties[1].id;
     args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args5);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
 
     // Lift up the stylus pointer
     NotifyMotionArgs args6 =
@@ -701,7 +687,7 @@
     args6.pointerProperties[0].id = args4.pointerProperties[1].id;
     args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     mBlocker->notifyMotion(&args6);
-    mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
 }
 
 using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 455a1e2..f4ecba2 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -46,3 +46,105 @@
        cc: ["android-framework-input@google.com"],
     },
 }
+
+cc_defaults {
+    name: "inputflinger_fuzz_defaults",
+    defaults: [
+        "inputflinger_defaults",
+    ],
+    include_dirs: [
+        "frameworks/native/services/inputflinger",
+    ],
+    shared_libs: [
+        "android.hardware.input.classifier@1.0",
+        "android.hardware.input.processor-V1-ndk",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libui",
+        "libinput",
+        "libinputflinger",
+        "libinputreader",
+        "libinputflinger_base",
+        "libstatslog",
+    ],
+    header_libs: [
+        "libbatteryservice_headers",
+        "libinputreader_headers",
+    ],
+    fuzz_config: {
+       cc: ["android-framework-input@google.com"],
+    },
+}
+
+cc_fuzz {
+    name: "inputflinger_cursor_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "CursorInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_keyboard_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "KeyboardInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_multitouch_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "MultiTouchInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_switch_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "SwitchInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_input_reader_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "InputReaderFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_blocking_queue_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "BlockingQueueFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_input_classifier_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "InputClassifierFuzzer.cpp",
+    ],
+}
diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
new file mode 100644
index 0000000..d2595bf
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+#include <thread>
+#include "BlockingQueue.h"
+
+// Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory.
+static constexpr size_t MAX_CAPACITY = 1024;
+
+namespace android {
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    size_t capacity = fdp.ConsumeIntegralInRange<size_t>(1, MAX_CAPACITY);
+    size_t filled = 0;
+    BlockingQueue<int32_t> queue(capacity);
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    size_t numPushes = fdp.ConsumeIntegralInRange<size_t>(0, capacity + 1);
+                    for (size_t i = 0; i < numPushes; i++) {
+                        queue.push(fdp.ConsumeIntegral<int32_t>());
+                    }
+                    filled = std::min(capacity, filled + numPushes);
+                },
+                [&]() -> void {
+                    // Pops blocks if it is empty, so only pop up to num elements inserted.
+                    size_t numPops = fdp.ConsumeIntegralInRange<size_t>(0, filled);
+                    for (size_t i = 0; i < numPops; i++) {
+                        queue.pop();
+                    }
+                    filled > numPops ? filled -= numPops : filled = 0;
+                },
+                [&]() -> void {
+                    queue.clear();
+                    filled = 0;
+                },
+                [&]() -> void {
+                    int32_t eraseElement = fdp.ConsumeIntegral<int32_t>();
+                    queue.erase([&](int32_t element) {
+                        if (element == eraseElement) {
+                            filled--;
+                            return true;
+                        }
+                        return false;
+                    });
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
new file mode 100644
index 0000000..4b542aa
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <CursorInputMapper.h>
+#include <FuzzContainer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); },
+             [&]() -> void { fuzzer.addProperty("cursor.mode", "navigation"); },
+             [&]() -> void {
+                 fuzzer.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("cursor.orientationAware",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                     fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void {
+                    int32_t type, code;
+                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    mapper.process(&rawEvent);
+                },
+                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    mapper.getAssociatedDisplayId();
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
new file mode 100644
index 0000000..62615d0
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -0,0 +1,82 @@
+/*
+ * 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 <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+class FuzzContainer {
+    std::shared_ptr<FuzzEventHub> mFuzzEventHub;
+    sp<FuzzInputReaderPolicy> mFuzzPolicy;
+    std::unique_ptr<FuzzInputListener> mFuzzListener;
+    std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
+    std::unique_ptr<InputDevice> mFuzzDevice;
+    InputReaderConfiguration mPolicyConfig;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzContainer(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(fdp) {
+        // Setup parameters.
+        std::string deviceName = mFdp->ConsumeRandomLengthString(16);
+        std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
+        int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5);
+        int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5);
+
+        // Create mocked objects.
+        mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
+        mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
+        mFuzzListener = std::make_unique<FuzzInputListener>();
+        mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
+                                                                *mFuzzListener, mFdp);
+
+        InputDeviceIdentifier identifier;
+        identifier.name = deviceName;
+        identifier.location = deviceLocation;
+        mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
+                                                    identifier);
+        mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
+    }
+
+    ~FuzzContainer() {}
+
+    void configureDevice() {
+        nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
+        mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
+        mFuzzDevice->reset(arbitraryTime);
+    }
+
+    void addProperty(std::string key, std::string value) {
+        mFuzzEventHub->addProperty(key, value);
+        configureDevice();
+    }
+
+    InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
+
+    template <class T, typename... Args>
+    T& getMapper(Args... args) {
+        T& mapper = mFuzzDevice->addMapper<T>(mFdp->ConsumeIntegral<int32_t>(), args...);
+        configureDevice();
+        return mapper;
+    }
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
new file mode 100644
index 0000000..c407cff
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "InputCommonConverter.h"
+#include "InputProcessor.h"
+
+namespace android {
+
+static constexpr int32_t MAX_AXES = 64;
+
+// Used by two fuzz operations and a bit lengthy, so pulled out into a function.
+NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) {
+    // Create a basic motion event for testing
+    PointerProperties properties;
+    properties.id = 0;
+    properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+    PointerCoords coords;
+    coords.clear();
+    for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) {
+        coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>());
+    }
+
+    const nsecs_t downTime = 2;
+    const nsecs_t readTime = downTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
+    NotifyMotionArgs motionArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                downTime /*eventTime*/, readTime,
+                                fdp.ConsumeIntegral<int32_t>() /*deviceId*/, AINPUT_SOURCE_ANY,
+                                ADISPLAY_ID_DEFAULT,
+                                fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                AMOTION_EVENT_ACTION_DOWN,
+                                fdp.ConsumeIntegral<int32_t>() /*actionButton*/,
+                                fdp.ConsumeIntegral<int32_t>() /*flags*/, AMETA_NONE,
+                                fdp.ConsumeIntegral<int32_t>() /*buttonState*/,
+                                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                1 /*pointerCount*/, &properties, &coords,
+                                fdp.ConsumeFloatingPoint<float>() /*xPrecision*/,
+                                fdp.ConsumeFloatingPoint<float>() /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
+    return motionArgs;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    std::unique_ptr<FuzzInputListener> mFuzzListener = std::make_unique<FuzzInputListener>();
+    std::unique_ptr<InputProcessorInterface> mClassifier =
+            std::make_unique<InputProcessor>(*mFuzzListener);
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    // SendToNextStage_NotifyConfigurationChangedArgs
+                    NotifyConfigurationChangedArgs
+                            args(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                 fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/);
+                    mClassifier->notifyConfigurationChanged(&args);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyKeyArgs
+                    const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t readTime =
+                            eventTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
+                    NotifyKeyArgs keyArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                          eventTime, readTime,
+                                          fdp.ConsumeIntegral<int32_t>() /*deviceId*/,
+                                          AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
+                                          fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                          AKEY_EVENT_ACTION_DOWN,
+                                          fdp.ConsumeIntegral<int32_t>() /*flags*/, AKEYCODE_HOME,
+                                          fdp.ConsumeIntegral<int32_t>() /*scanCode*/, AMETA_NONE,
+                                          fdp.ConsumeIntegral<nsecs_t>() /*downTime*/);
+
+                    mClassifier->notifyKey(&keyArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyMotionArgs
+                    NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+                    mClassifier->notifyMotion(&motionArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifySwitchArgs
+                    NotifySwitchArgs switchArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                                fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*switchValues*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*switchMask*/);
+
+                    mClassifier->notifySwitch(&switchArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyDeviceResetArgs
+                    NotifyDeviceResetArgs resetArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                                    fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
+                                                    fdp.ConsumeIntegral<int32_t>() /*deviceId*/);
+
+                    mClassifier->notifyDeviceReset(&resetArgs);
+                },
+                [&]() -> void {
+                    // InputClassifierConverterTest
+                    const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+                    aidl::android::hardware::input::common::MotionEvent motionEvent =
+                            notifyMotionArgsToHalMotionEvent(motionArgs);
+                },
+        })();
+    }
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
new file mode 100644
index 0000000..f5bd297
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -0,0 +1,284 @@
+/*
+ * 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 <InputReader.h>
+#include <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <input/InputDevice.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+
+constexpr InputDeviceSensorType kInputDeviceSensorType[] = {
+        InputDeviceSensorType::ACCELEROMETER,
+        InputDeviceSensorType::MAGNETIC_FIELD,
+        InputDeviceSensorType::ORIENTATION,
+        InputDeviceSensorType::GYROSCOPE,
+        InputDeviceSensorType::LIGHT,
+        InputDeviceSensorType::PRESSURE,
+        InputDeviceSensorType::TEMPERATURE,
+        InputDeviceSensorType::PROXIMITY,
+        InputDeviceSensorType::GRAVITY,
+        InputDeviceSensorType::LINEAR_ACCELERATION,
+        InputDeviceSensorType::ROTATION_VECTOR,
+        InputDeviceSensorType::RELATIVE_HUMIDITY,
+        InputDeviceSensorType::AMBIENT_TEMPERATURE,
+        InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED,
+        InputDeviceSensorType::GAME_ROTATION_VECTOR,
+        InputDeviceSensorType::GYROSCOPE_UNCALIBRATED,
+        InputDeviceSensorType::SIGNIFICANT_MOTION,
+};
+
+class FuzzInputReader : public InputReaderInterface {
+public:
+    FuzzInputReader(std::shared_ptr<EventHubInterface> fuzzEventHub,
+                    const sp<InputReaderPolicyInterface>& fuzzPolicy,
+                    InputListenerInterface& fuzzListener) {
+        reader = std::make_unique<InputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
+    }
+
+    void dump(std::string& dump) { reader->dump(dump); }
+
+    void monitor() { reader->monitor(); }
+
+    bool isInputDeviceEnabled(int32_t deviceId) { return reader->isInputDeviceEnabled(deviceId); }
+
+    status_t start() { return reader->start(); }
+
+    status_t stop() { return reader->stop(); }
+
+    std::vector<InputDeviceInfo> getInputDevices() const { return reader->getInputDevices(); }
+
+    int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
+        return reader->getScanCodeState(deviceId, sourceMask, scanCode);
+    }
+
+    int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
+        return reader->getKeyCodeState(deviceId, sourceMask, keyCode);
+    }
+
+    int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
+        return reader->getSwitchState(deviceId, sourceMask, sw);
+    }
+
+    void toggleCapsLockState(int32_t deviceId) { reader->toggleCapsLockState(deviceId); }
+
+    bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
+                 uint8_t* outFlags) {
+        return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags);
+    }
+
+    void requestRefreshConfiguration(uint32_t changes) {
+        reader->requestRefreshConfiguration(changes);
+    }
+
+    void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+                 int32_t token) {
+        reader->vibrate(deviceId, sequence, repeat, token);
+    }
+
+    void cancelVibrate(int32_t deviceId, int32_t token) { reader->cancelVibrate(deviceId, token); }
+
+    bool isVibrating(int32_t deviceId) { return reader->isVibrating(deviceId); }
+
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) {
+        return reader->getVibratorIds(deviceId);
+    }
+
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId) {
+        return reader->getBatteryCapacity(deviceId);
+    }
+
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId) {
+        return reader->getBatteryStatus(deviceId);
+    }
+
+    std::optional<std::string> getBatteryDevicePath(int32_t deviceId) {
+        return reader->getBatteryDevicePath(deviceId);
+    }
+
+    std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) {
+        return reader->getLights(deviceId);
+    }
+
+    std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) {
+        return reader->getSensors(deviceId);
+    }
+
+    bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+        return reader->canDispatchToDisplay(deviceId, displayId);
+    }
+
+    bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                      std::chrono::microseconds samplingPeriod,
+                      std::chrono::microseconds maxBatchReportLatency) {
+        return reader->enableSensor(deviceId, sensorType, samplingPeriod, maxBatchReportLatency);
+    }
+
+    void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+        return reader->disableSensor(deviceId, sensorType);
+    }
+
+    void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+        return reader->flushSensor(deviceId, sensorType);
+    }
+
+    bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
+        return reader->setLightColor(deviceId, lightId, color);
+    }
+
+    bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) {
+        return reader->setLightPlayerId(deviceId, lightId, playerId);
+    }
+
+    std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) {
+        return reader->getLightColor(deviceId, lightId);
+    }
+
+    std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) {
+        return reader->getLightPlayerId(deviceId, lightId);
+    }
+
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+        return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
+    }
+
+private:
+    std::unique_ptr<InputReaderInterface> reader;
+};
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+
+    FuzzInputListener fuzzListener;
+    sp<FuzzInputReaderPolicy> fuzzPolicy = sp<FuzzInputReaderPolicy>::make(fdp);
+    std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
+    std::unique_ptr<FuzzInputReader> reader =
+            std::make_unique<FuzzInputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
+    fuzzEventHub->addEvents(fdp);
+    size_t patternCount = fdp->ConsumeIntegralInRange<size_t>(1, 260);
+    VibrationSequence pattern(patternCount);
+    for (size_t i = 0; i < patternCount; ++i) {
+        VibrationElement element(i);
+        element.addChannel(fdp->ConsumeIntegral<int32_t>() /* vibratorId */,
+                           fdp->ConsumeIntegral<uint8_t>() /* amplitude */);
+        pattern.addElement(element);
+    }
+    reader->vibrate(fdp->ConsumeIntegral<int32_t>(), pattern,
+                    fdp->ConsumeIntegral<ssize_t>() /*repeat*/,
+                    fdp->ConsumeIntegral<int32_t>() /*token*/);
+    reader->start();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    std::string dump;
+                    reader->dump(dump);
+                },
+                [&]() -> void { reader->monitor(); },
+                [&]() -> void { reader->getInputDevices(); },
+                [&]() -> void { reader->isInputDeviceEnabled(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    reader->getScanCodeState(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<uint32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getKeyCodeState(fdp->ConsumeIntegral<int32_t>(),
+                                            fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getSwitchState(fdp->ConsumeIntegral<int32_t>(),
+                                           fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024);
+                    std::vector<uint8_t> outFlags(count);
+                    std::vector<int32_t> keyCodes;
+                    for (size_t i = 0; i < count; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    reader->hasKeys(fdp->ConsumeIntegral<int32_t>(),
+                                    fdp->ConsumeIntegral<uint32_t>(), keyCodes, outFlags.data());
+                },
+                [&]() -> void {
+                    reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void {
+                    reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->canDispatchToDisplay(fdp->ConsumeIntegral<int32_t>(),
+                                                 fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getKeyCodeForKeyLocation(fdp->ConsumeIntegral<int32_t>(),
+                                                     fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void { reader->getBatteryCapacity(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getBatteryStatus(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getBatteryDevicePath(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getLights(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getSensors(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    reader->getLightPlayerId(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getLightColor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->setLightPlayerId(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->setLightColor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->flushSensor(fdp->ConsumeIntegral<int32_t>(),
+                                        fdp->PickValueInArray<InputDeviceSensorType>(
+                                                kInputDeviceSensorType));
+                },
+                [&]() -> void {
+                    reader->disableSensor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->PickValueInArray<InputDeviceSensorType>(
+                                                  kInputDeviceSensorType));
+                },
+                [&]() -> void {
+                    reader->enableSensor(fdp->ConsumeIntegral<int32_t>(),
+                                         fdp->PickValueInArray<InputDeviceSensorType>(
+                                                 kInputDeviceSensorType),
+                                         std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
+                                         std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
+                },
+        })();
+    }
+
+    reader->stop();
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
new file mode 100644
index 0000000..c48a099
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <FuzzContainer.h>
+#include <KeyboardInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+const int32_t kMaxKeycodes = 100;
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.orientationAware",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.doNotWakeByDefault",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.handlesKeyRepeat",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    KeyboardInputMapper& mapper =
+            fuzzer.getMapper<KeyboardInputMapper>(fdp->ConsumeIntegral<uint32_t>(),
+                                                  fdp->ConsumeIntegral<int32_t>());
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void {
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                     fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    int32_t type, code;
+                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    std::vector<int32_t> keyCodes;
+                    int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes);
+                    for (int32_t i = 0; i < numBytes; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes,
+                                                 nullptr);
+                },
+                [&]() -> void { mapper.getMetaState(); },
+                [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { mapper.getAssociatedDisplayId(); },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
new file mode 100644
index 0000000..53a7b16
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -0,0 +1,372 @@
+/*
+ * 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 <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
+using android::hardware::input::InputDeviceCountryCode;
+
+constexpr size_t kValidTypes[] = {EV_SW,
+                                  EV_SYN,
+                                  SYN_REPORT,
+                                  EV_ABS,
+                                  EV_KEY,
+                                  EV_MSC,
+                                  EV_REL,
+                                  android::EventHubInterface::DEVICE_ADDED,
+                                  android::EventHubInterface::DEVICE_REMOVED,
+                                  android::EventHubInterface::FINISHED_DEVICE_SCAN};
+
+constexpr size_t kValidCodes[] = {
+        SYN_REPORT,
+        ABS_MT_SLOT,
+        SYN_MT_REPORT,
+        ABS_MT_POSITION_X,
+        ABS_MT_POSITION_Y,
+        ABS_MT_TOUCH_MAJOR,
+        ABS_MT_TOUCH_MINOR,
+        ABS_MT_WIDTH_MAJOR,
+        ABS_MT_WIDTH_MINOR,
+        ABS_MT_ORIENTATION,
+        ABS_MT_TRACKING_ID,
+        ABS_MT_PRESSURE,
+        ABS_MT_DISTANCE,
+        ABS_MT_TOOL_TYPE,
+        SYN_MT_REPORT,
+        MSC_SCAN,
+        REL_X,
+        REL_Y,
+        REL_WHEEL,
+        REL_HWHEEL,
+        BTN_LEFT,
+        BTN_RIGHT,
+        BTN_MIDDLE,
+        BTN_BACK,
+        BTN_SIDE,
+        BTN_FORWARD,
+        BTN_EXTRA,
+        BTN_TASK,
+};
+
+constexpr InputDeviceCountryCode kCountryCodes[] = {
+        InputDeviceCountryCode::INVALID,
+        InputDeviceCountryCode::NOT_SUPPORTED,
+        InputDeviceCountryCode::ARABIC,
+        InputDeviceCountryCode::BELGIAN,
+        InputDeviceCountryCode::CANADIAN_BILINGUAL,
+        InputDeviceCountryCode::CANADIAN_FRENCH,
+        InputDeviceCountryCode::CZECH_REPUBLIC,
+        InputDeviceCountryCode::DANISH,
+        InputDeviceCountryCode::FINNISH,
+        InputDeviceCountryCode::FRENCH,
+        InputDeviceCountryCode::GERMAN,
+        InputDeviceCountryCode::GREEK,
+        InputDeviceCountryCode::HEBREW,
+        InputDeviceCountryCode::HUNGARY,
+        InputDeviceCountryCode::INTERNATIONAL,
+        InputDeviceCountryCode::ITALIAN,
+        InputDeviceCountryCode::JAPAN,
+        InputDeviceCountryCode::KOREAN,
+        InputDeviceCountryCode::LATIN_AMERICAN,
+        InputDeviceCountryCode::DUTCH,
+        InputDeviceCountryCode::NORWEGIAN,
+        InputDeviceCountryCode::PERSIAN,
+        InputDeviceCountryCode::POLAND,
+        InputDeviceCountryCode::PORTUGUESE,
+        InputDeviceCountryCode::RUSSIA,
+        InputDeviceCountryCode::SLOVAKIA,
+        InputDeviceCountryCode::SPANISH,
+        InputDeviceCountryCode::SWEDISH,
+        InputDeviceCountryCode::SWISS_FRENCH,
+        InputDeviceCountryCode::SWISS_GERMAN,
+        InputDeviceCountryCode::SWITZERLAND,
+        InputDeviceCountryCode::TAIWAN,
+        InputDeviceCountryCode::TURKISH_Q,
+        InputDeviceCountryCode::UK,
+        InputDeviceCountryCode::US,
+        InputDeviceCountryCode::YUGOSLAVIA,
+        InputDeviceCountryCode::TURKISH_F,
+};
+
+constexpr size_t kMaxSize = 256;
+
+namespace android {
+
+class FuzzEventHub : public EventHubInterface {
+    InputDeviceIdentifier mIdentifier;
+    std::vector<TouchVideoFrame> mVideoFrames;
+    PropertyMap mFuzzConfig;
+    size_t mCount = 0;
+    std::array<RawEvent, kMaxSize> mBuf;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
+    ~FuzzEventHub() {}
+    void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
+    void addEvents(std::shared_ptr<FuzzedDataProvider> fdp) {
+        mCount = fdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
+
+        for (size_t i = 0; i < mCount; ++i) {
+            int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+            int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+            mBuf[i] = {fdp->ConsumeIntegral<nsecs_t>(),
+                       fdp->ConsumeIntegral<nsecs_t>(),
+                       fdp->ConsumeIntegral<int32_t>(),
+                       type,
+                       code,
+                       fdp->ConsumeIntegral<int32_t>()};
+        }
+    }
+
+    ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
+        return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
+    }
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
+        return mIdentifier;
+    }
+    int32_t getDeviceControllerNumber(int32_t deviceId) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
+        *outConfiguration = mFuzzConfig;
+    }
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
+    bool hasInputProperty(int32_t deviceId, int property) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasMscEvent(int32_t deviceId, int mscEvent) const override { return mFdp->ConsumeBool(); }
+    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 {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    void setExcludedDevices(const std::vector<std::string>& devices) override {}
+    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override {
+        for (size_t i = 0; i < mCount; ++i) buffer[i] = mBuf[i];
+
+        return mCount;
+    }
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; }
+
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) const override {
+        return base::ResultError("Fuzzer", UNKNOWN_ERROR);
+    };
+    // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+    // containing the raw info of the sysfs node structure.
+    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override { return {}; }
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t BatteryId) const override {
+        return std::nullopt;
+    };
+
+    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override { return {}; };
+    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override{};
+    std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+            int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    void setLightIntensities(int32_t deviceId, int32_t lightId,
+                             std::unordered_map<LightColor, int32_t> intensities) override{};
+
+    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
+        return mFdp->PickValueInArray<InputDeviceCountryCode>(kCountryCodes);
+    };
+
+    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                  int32_t* outValue) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+                               uint8_t* outFlags) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasLed(int32_t deviceId, int32_t led) const override { return mFdp->ConsumeBool(); }
+    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 deviceId) const override {
+        return nullptr;
+    }
+    bool setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) override {
+        return mFdp->ConsumeBool();
+    }
+    void vibrate(int32_t deviceId, const VibrationElement& effect) override {}
+    void cancelVibrate(int32_t deviceId) override {}
+
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return {}; };
+
+    /* Query battery level. */
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId, int32_t batteryId) const override {
+        return std::nullopt;
+    };
+
+    /* Query battery status. */
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const override {
+        return std::nullopt;
+    };
+
+    void requestReopenDevices() override {}
+    void wake() override {}
+    void dump(std::string& dump) const override {}
+    void monitor() const override {}
+    bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); }
+    status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+    status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+};
+
+class FuzzPointerController : public PointerControllerInterface {
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzPointerController(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {}
+    ~FuzzPointerController() {}
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
+        return mFdp->ConsumeBool();
+    }
+    void move(float deltaX, float deltaY) override {}
+    void setButtonState(int32_t buttonState) override {}
+    int32_t getButtonState() const override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void setPosition(float x, float y) override {}
+    void getPosition(float* outX, float* outY) const override {}
+    void fade(Transition transition) override {}
+    void unfade(Transition transition) override {}
+    void setPresentation(Presentation presentation) override {}
+    void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                  BitSet32 spotIdBits, int32_t displayId) override {}
+    void clearSpots() override {}
+    int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void setDisplayViewport(const DisplayViewport& displayViewport) override {}
+};
+
+class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
+    TouchAffineTransformation mTransform;
+    std::shared_ptr<FuzzPointerController> mPointerController;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+protected:
+    ~FuzzInputReaderPolicy() {}
+
+public:
+    FuzzInputReaderPolicy(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {
+        mPointerController = std::make_shared<FuzzPointerController>(mFdp);
+    }
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
+        return mPointerController;
+    }
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier) override {
+        return nullptr;
+    }
+    std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
+        return mFdp->ConsumeRandomLengthString(32);
+    }
+    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+                                                           int32_t surfaceRotation) override {
+        return mTransform;
+    }
+    void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+};
+
+class FuzzInputListener : public virtual InputListenerInterface {
+public:
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override {}
+    void notifyKey(const NotifyKeyArgs* args) override {}
+    void notifyMotion(const NotifyMotionArgs* args) override {}
+    void notifySwitch(const NotifySwitchArgs* args) override {}
+    void notifySensor(const NotifySensorArgs* args) override{};
+    void notifyVibratorState(const NotifyVibratorStateArgs* args) override{};
+    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override {}
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override{};
+};
+
+class FuzzInputReaderContext : public InputReaderContext {
+    std::shared_ptr<EventHubInterface> mEventHub;
+    sp<InputReaderPolicyInterface> mPolicy;
+    InputListenerInterface& mListener;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
+                           const sp<InputReaderPolicyInterface>& policy,
+                           InputListenerInterface& listener,
+                           std::shared_ptr<FuzzedDataProvider> mFdp)
+          : mEventHub(eventHub), mPolicy(policy), mListener(listener), mFdp(mFdp) {}
+    ~FuzzInputReaderContext() {}
+    void updateGlobalMetaState() override {}
+    int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
+    void disableVirtualKeysUntil(nsecs_t time) override {}
+    bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override {
+        return mFdp->ConsumeBool();
+    }
+    void fadePointer() override {}
+    std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override {
+        return mPolicy->obtainPointerController(0);
+    }
+    void requestTimeoutAtTime(nsecs_t when) override {}
+    int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
+    void dispatchExternalStylusState(const StylusState& outState) override {}
+    InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
+    InputListenerInterface& getListener() override { return mListener; }
+    EventHubInterface* getEventHub() override { return mEventHub.get(); }
+    int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); }
+
+    void updateLedMetaState(int32_t metaState) override{};
+    int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
new file mode 100644
index 0000000..59b0642
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 <FuzzContainer.h>
+#include <MultiTouchInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+const int32_t kMaxKeycodes = 100;
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.isSummed",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.calibration",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.pressure.scale",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.calibration",
+                                    fdp->ConsumeBool() ? "diameter" : "area");
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.pressure.calibration",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void {
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                     fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    std::vector<int32_t> keyCodes;
+                    int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes);
+                    for (int32_t i = 0; i < numBytes; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes,
+                                                 nullptr);
+                },
+                [&]() -> void {
+                    mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
+                                       fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void { mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    StylusState state{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeFloatingPoint<float>(),
+                                      fdp->ConsumeIntegral<uint32_t>(),
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    mapper.updateExternalStylusState(state);
+                },
+                [&]() -> void { mapper.getAssociatedDisplayId(); },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
new file mode 100644
index 0000000..e76bd72
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <FuzzContainer.h>
+#include <SwitchInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index de050e0..10ca990 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -39,7 +39,6 @@
 #include <thread>
 
 using namespace android::hardware::sensors;
-using android::hardware::Return;
 using android::util::ProtoOutputStream;
 
 namespace android {
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 2dd12e9..291c770 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "SensorDevice.h"
 #include "SensorDirectConnection.h"
 #include <android/util/ProtoOutputStream.h>
 #include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
+#include "SensorDevice.h"
 
 #define UNUSED(x) (void)(x)
 
@@ -51,7 +51,7 @@
     stopAll();
     mService->cleanupConnection(this);
     if (mMem.handle != nullptr) {
-        native_handle_close(mMem.handle);
+        native_handle_close_with_tag(mMem.handle);
         native_handle_delete(const_cast<struct native_handle*>(mMem.handle));
     }
     mDestroyed = true;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index e0a4f03..21d6b6b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -16,7 +16,6 @@
 #include <android-base/strings.h>
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android/util/ProtoOutputStream.h>
-#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <binder/ActivityManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
@@ -25,6 +24,7 @@
 #include <cutils/ashmem.h>
 #include <cutils/misc.h>
 #include <cutils/properties.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 #include <hardware_legacy/power.h>
 #include <log/log.h>
@@ -1475,6 +1475,7 @@
     if (!clone) {
         return nullptr;
     }
+    native_handle_set_fdsan_tag(clone);
 
     sp<SensorDirectConnection> conn;
     SensorDevice& dev(SensorDevice::getInstance());
@@ -1488,7 +1489,7 @@
     }
 
     if (conn == nullptr) {
-        native_handle_close(clone);
+        native_handle_close_with_tag(clone);
         native_handle_delete(clone);
     } else {
         // add to list of direct connections
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index caf7f03..b00d1a7 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -89,6 +89,17 @@
 
     // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
     printf("createInvalidDirectChannel=%d\n", ret);
+
+    // Secondary test: correct channel creation & destruction (should print 0)
+    ret = mgr.createDirectChannel(kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER,
+                                  resourceHandle);
+    printf("createValidDirectChannel=%d\n", ret);
+
+    // Third test: double-destroy (should not crash)
+    mgr.destroyDirectChannel(ret);
+    AHardwareBuffer_release(hardwareBuffer);
+    printf("duplicate destroyDirectChannel...\n");
+    mgr.destroyDirectChannel(ret);
 }
 
 int main() {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5e9fe65..b911ae7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -141,16 +141,16 @@
     name: "libsurfaceflinger_sources",
     srcs: [
         "BackgroundExecutor.cpp",
-        "ClientCache.cpp",
         "Client.cpp",
-        "EffectLayer.cpp",
+        "ClientCache.cpp",
+        "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/AidlComposerHal.cpp",
-        "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/ComposerHal.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
+        "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
         "DisplayRenderArea.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 16cb41b..5e84be1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@
     // similar requests if needed.
     virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
+    // Sends the brightness setting to HWC
+    virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+
 protected:
     ~Display() = default;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 9753a6c..a738da0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -53,31 +53,8 @@
     // Called before composition starts. Should return true if this layer has
     // pending updates which would require an extra display refresh cycle to
     // process.
-    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
-
-    // Used with latchCompositionState()
-    enum class StateSubset {
-        // Gets the basic geometry (bounds, transparent region, visibility,
-        // transforms, alpha) for the layer, for computing visibility and
-        // coverage.
-        BasicGeometry,
-
-        // Gets the full geometry (crops, buffer transforms, metadata) and
-        // content (buffer or color) state for the layer.
-        GeometryAndContent,
-
-        // Gets the per frame content (buffer or color) state for the layer.
-        Content,
-
-        // Gets the cursor state for the layer.
-        Cursor,
-    };
-
-    // Prepares the output-independent composition state for the layer. The
-    // StateSubset argument selects what portion of the state is actually needed
-    // by the CompositionEngine code, since computing everything may be
-    // expensive.
-    virtual void prepareCompositionState(StateSubset) = 0;
+    virtual bool onPreComposition(nsecs_t refreshStartTime,
+                                  bool updatingOutputGeometryThisFrame) = 0;
 
     struct ClientCompositionTargetSettings {
         enum class BlurSetting {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2203639..874b330 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -262,9 +262,6 @@
     // Presents the output, finalizing all composition details
     virtual void present(const CompositionRefreshArgs&) = 0;
 
-    // Latches the front-end layer state for each output layer
-    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
-
     // Enables predicting composition strategy to run client composition earlier
     virtual void setPredictCompositionStrategy(bool) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 386808d..0907926 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -51,8 +51,6 @@
     // Debugging
     void dump(std::string&) const override;
 
-    void updateLayerStateFromFE(CompositionRefreshArgs& args);
-
     // Testing
     void setNeedsAnotherUpdateForTest(bool);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index fa7bc5d..33a10a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -72,6 +72,7 @@
             const compositionengine::DisplayColorProfileCreationArgs&) override;
     void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
     void createClientCompositionCache(uint32_t cacheSize) override;
+    void applyDisplayBrightness(const bool applyImmediately) override;
 
     // Internal helpers used by chooseCompositionStrategy()
     using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 38a391b..23d5570 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -89,7 +89,6 @@
                                     compositionengine::Output::CoverageState&) override;
     void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
 
-    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
     void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void planComposition() override;
     void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 72e6f3b..7e99ec2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@
     MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
     MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
     MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+    MOCK_METHOD1(applyDisplayBrightness, void(const bool));
     MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 2b704e6..be0dbce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,9 +42,8 @@
 
     MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
 
-    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+    MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool));
 
-    MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
     MOCK_CONST_METHOD1(prepareClientComposition,
                        std::optional<compositionengine::LayerFE::LayerSettings>(
                                compositionengine::LayerFE::ClientCompositionTargetSettings&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 2a04949..7592cac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -91,7 +91,6 @@
                  void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
     MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(planComposition, void());
     MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 6203dc6..855507e 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -105,8 +105,6 @@
         }
     }
 
-    updateLayerStateFromFE(args);
-
     for (const auto& output : args.outputs) {
         output->present(args);
     }
@@ -119,8 +117,6 @@
     for (const auto& output : args.outputs) {
         for (auto* layer : output->getOutputLayersOrderedByZ()) {
             if (layer->isHardwareCursor()) {
-                // Latch the cursor composition state from each front-end layer.
-                layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
                 layer->writeCursorPositionToHWC();
             }
         }
@@ -136,7 +132,7 @@
     mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     for (auto& layer : args.layers) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
+        if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) {
             needsAnotherUpdate = true;
         }
     }
@@ -152,12 +148,5 @@
     mNeedsAnotherUpdate = value;
 }
 
-void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
-    // Update the composition state from each front-end layer
-    for (const auto& output : args.outputs) {
-        output->updateLayerStateFromFE(args);
-    }
-}
-
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 0ebbcd0..0b69d44 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -203,6 +203,24 @@
     setReleasedLayers(std::move(releasedLayers));
 }
 
+void Display::applyDisplayBrightness(const bool applyImmediately) {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId());
+    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
+        physicalDisplayId && getState().displayBrightness) {
+        const status_t result =
+                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+                                         getState().displayBrightnessNits,
+                                         Hwc2::Composer::DisplayBrightnessOptions{
+                                                 .applyImmediately = applyImmediately})
+                        .get();
+        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+                 getName().c_str(), result, strerror(-result));
+    }
+    // Clear out the display brightness now that it's been communicated to composer.
+    editState().displayBrightness.reset();
+}
+
 void Display::beginFrame() {
     Output::beginFrame();
 
@@ -212,20 +230,7 @@
         return;
     }
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
-        physicalDisplayId && getState().displayBrightness) {
-        const status_t result =
-                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
-                                         getState().displayBrightnessNits,
-                                         Hwc2::Composer::DisplayBrightnessOptions{
-                                                 .applyImmediately = false})
-                        .get();
-        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
-                 getName().c_str(), result, strerror(-result));
-    }
-    // Clear out the display brightness now that it's been communicated to composer.
-    editState().displayBrightness.reset();
+    applyDisplayBrightness(false);
 }
 
 bool Display::chooseCompositionStrategy(
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b5894cf..e3f3680 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -501,7 +501,6 @@
     // appear on multiple outputs.
     if (!coverage.latchedLayers.count(layerFE)) {
         coverage.latchedLayers.insert(layerFE);
-        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
     }
 
     // Only consider the layers on this output
@@ -725,14 +724,6 @@
     // The base class does nothing with this call.
 }
 
-void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
-    for (auto* layer : getOutputLayersOrderedByZ()) {
-        layer->getLayerFE().prepareCompositionState(
-                args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
-                                               : LayerFE::StateSubset::Content);
-    }
-}
-
 void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index de9de01..b570979 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -108,12 +108,6 @@
     EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
     EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
 
-    // The next step in presenting is to make sure all outputs have the latest
-    // state from the front-end (SurfaceFlinger).
-    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
-
     // The last step is to actually present each output.
     EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
     EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
@@ -175,21 +169,18 @@
     {
         InSequence seq;
         EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
     }
 
@@ -222,9 +213,12 @@
     nsecs_t ts1 = 0;
     nsecs_t ts2 = 0;
     nsecs_t ts3 = 0;
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -238,9 +232,9 @@
 }
 
 TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mEngine.setNeedsAnotherUpdateForTest(true);
 
@@ -255,9 +249,9 @@
 
 TEST_F(CompositionTestPreComposition,
        preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ace2864..eb209e9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -758,56 +758,6 @@
 }
 
 /*
- * Output::updateLayerStateFromFE()
- */
-
-using OutputUpdateLayerStateFromFETest = OutputTest;
-
-TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
-    CompositionRefreshArgs refreshArgs;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = false;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = true;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-/*
  * Output::updateAndWriteCompositionState()
  */
 
@@ -1536,9 +1486,6 @@
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer.layerFE,
-                prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
-
     mGeomSnapshots.clear();
 
     ensureOutputLayerIfVisible();
diff --git a/services/surfaceflinger/Display/DisplayMap.h b/services/surfaceflinger/Display/DisplayMap.h
new file mode 100644
index 0000000..baf0da9
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayMap.h
@@ -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.
+ */
+
+#pragma once
+
+#include <ftl/small_map.h>
+
+namespace android::display {
+
+// The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
+
+template <typename Key, typename Value>
+using DisplayMap = ftl::SmallMap<Key, Value, 5>;
+
+template <typename Key, typename Value>
+using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
new file mode 100644
index 0000000..b4f104a
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 <functional>
+#include <utility>
+
+#include <ftl/algorithm.h>
+#include <ftl/enum.h>
+
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
+                                 ui::DisplayConnectionType connectionType,
+                                 DisplayModes&& displayModes,
+                                 std::optional<DeviceProductInfo>&& deviceProductInfo)
+      : mDisplayId(displayId),
+        mConnectionType(connectionType),
+        mDisplayModes(std::move(displayModes)),
+        mDeviceProductInfo(std::move(deviceProductInfo)) {}
+
+std::optional<DisplayModeId> DisplaySnapshot::translateModeId(hal::HWConfigId hwcId) const {
+    return ftl::find_if(mDisplayModes,
+                        [hwcId](const DisplayModes::value_type& pair) {
+                            return pair.second->getHwcId() == hwcId;
+                        })
+            .transform(&ftl::to_key<DisplayModes>);
+}
+
+void DisplaySnapshot::dump(std::string& out) const {
+    using namespace std::string_literals;
+
+    out += "   connectionType="s;
+    out += ftl::enum_string(mConnectionType);
+
+    out += "\n   deviceProductInfo="s;
+    if (mDeviceProductInfo) {
+        mDeviceProductInfo->dump(out);
+    } else {
+        out += "{}"s;
+    }
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
new file mode 100644
index 0000000..0279220
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <optional>
+#include <string>
+
+#include <ui/DisplayId.h>
+#include <ui/StaticDisplayInfo.h>
+
+#include "../DisplayHardware/DisplayMode.h"
+
+namespace android::display {
+
+// Immutable state of a physical display, captured on hotplug.
+class DisplaySnapshot {
+public:
+    DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&,
+                    std::optional<DeviceProductInfo>&&);
+
+    DisplaySnapshot(const DisplaySnapshot&) = delete;
+    DisplaySnapshot(DisplaySnapshot&&) = default;
+
+    PhysicalDisplayId displayId() const { return mDisplayId; }
+    ui::DisplayConnectionType connectionType() const { return mConnectionType; }
+
+    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
+
+    const auto& displayModes() const { return mDisplayModes; }
+    const auto& deviceProductInfo() const { return mDeviceProductInfo; }
+
+    void dump(std::string&) const;
+
+private:
+    const PhysicalDisplayId mDisplayId;
+    const ui::DisplayConnectionType mConnectionType;
+
+    // Effectively const except in move constructor.
+    DisplayModes mDisplayModes;
+    std::optional<DeviceProductInfo> mDeviceProductInfo;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
new file mode 100644
index 0000000..cba1014
--- /dev/null
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <functional>
+#include <utility>
+
+#include <binder/IBinder.h>
+#include <ui/DisplayId.h>
+#include <utils/StrongPointer.h>
+
+#include "DisplayMap.h"
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+// TODO(b/229877597): Replace with AIDL type.
+using DisplayToken = IBinder;
+
+class PhysicalDisplay {
+public:
+    template <typename... Args>
+    PhysicalDisplay(sp<DisplayToken> token, Args&&... args)
+          : mToken(std::move(token)), mSnapshot(std::forward<Args>(args)...) {}
+
+    PhysicalDisplay(const PhysicalDisplay&) = delete;
+    PhysicalDisplay(PhysicalDisplay&&) = default;
+
+    const sp<DisplayToken>& token() const { return mToken; }
+    const DisplaySnapshot& snapshot() const { return mSnapshot; }
+
+    // Transformers for PhysicalDisplays::get.
+
+    using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
+    SnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
+
+    bool isInternal() const {
+        return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal;
+    }
+
+    // Predicate for ftl::find_if on PhysicalDisplays.
+    static constexpr auto hasToken(const sp<DisplayToken>& token) {
+        return [&token](const std::pair<const PhysicalDisplayId, PhysicalDisplay>& pair) {
+            return pair.second.token() == token;
+        };
+    }
+
+private:
+    const sp<DisplayToken> mToken;
+
+    // Effectively const except in move constructor.
+    DisplaySnapshot mSnapshot;
+};
+
+using PhysicalDisplays = PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>;
+
+// Combinator for ftl::Optional<PhysicalDisplayId>::and_then.
+constexpr auto getPhysicalDisplay(const PhysicalDisplays& displays) {
+    return [&](PhysicalDisplayId id) { return displays.get(id); };
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2866a34..029e449 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -39,6 +39,7 @@
 #include <system/window.h>
 #include <ui/GraphicTypes.h>
 
+#include "Display/DisplaySnapshot.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
@@ -63,12 +64,10 @@
         mHwComposer(args.hwComposer),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mConnectionType(args.connectionType),
         mCompositionDisplay{args.compositionDisplay},
         mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
-        mSupportedModes(std::move(args.supportedModes)),
         mIsPrimary(args.isPrimary),
         mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
@@ -132,10 +131,6 @@
     }
 }
 
-void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
-    mDeviceProductInfo = std::move(info);
-}
-
 auto DisplayDevice::getInputInfo() const -> InputInfo {
     gui::DisplayInfo info;
     info.displayId = getLayerStack().id;
@@ -169,6 +164,15 @@
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+    if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+        if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+            mBrightness = *mStagedBrightness;
+        }
+        mStagedBrightness = std::nullopt;
+        getCompositionDisplay()->applyDisplayBrightness(true);
+    }
+
     mPowerMode = mode;
 
     getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
@@ -187,16 +191,20 @@
     return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
-void DisplayDevice::setActiveMode(DisplayModeId id) {
-    const auto mode = getMode(id);
-    LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
-    ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue());
-    mActiveMode = mode;
+void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
+    const auto modeOpt = snapshot.displayModes().get(modeId);
+    LOG_ALWAYS_FATAL_IF(!modeOpt, "Unknown mode");
+
+    mActiveMode = modeOpt->get();
+    const Fps fps = mActiveMode->getFps();
+
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
+
     if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->setActiveModeId(mActiveMode->getId());
+        mRefreshRateConfigs->setActiveModeId(modeId);
     }
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps());
+        mRefreshRateOverlay->changeRefreshRate(fps);
     }
 }
 
@@ -220,25 +228,6 @@
     return mActiveMode;
 }
 
-const DisplayModes& DisplayDevice::getSupportedModes() const {
-    return mSupportedModes;
-}
-
-DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
-    const DisplayModePtr nullMode;
-    return mSupportedModes.get(modeId).value_or(std::cref(nullMode));
-}
-
-std::optional<DisplayModeId> DisplayDevice::translateModeId(hal::HWConfigId hwcId) const {
-    const auto it =
-            std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
-                         [hwcId](const auto& pair) { return pair.second->getHwcId() == hwcId; });
-    if (it != mSupportedModes.end()) {
-        return it->second->getId();
-    }
-    return {};
-}
-
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
     const auto physicalId = getPhysicalId();
     if (!mHwComposer.isConnected(physicalId)) {
@@ -268,10 +257,10 @@
     return mCompositionDisplay->getState().dataspace;
 }
 
-void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerFilter({stack, isInternal()});
+void DisplayDevice::setLayerFilter(ui::LayerFilter filter) {
+    mCompositionDisplay->setLayerFilter(filter);
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->setLayerStack(stack);
+        mRefreshRateOverlay->setLayerStack(filter.layerStack);
     }
 }
 
@@ -343,11 +332,7 @@
 
     std::string name = "Display "s + to_string(getId()) + " ("s;
 
-    if (mConnectionType) {
-        name += isInternal() ? "internal"s : "external"s;
-    } else {
-        name += "virtual"s;
-    }
+    name += isVirtual() ? "virtual"s : "physical"s;
 
     if (isPrimary()) {
         name += ", primary"s;
@@ -361,15 +346,6 @@
 
     result += getDebugName();
 
-    if (!isVirtual()) {
-        result += "\n   deviceProductInfo="s;
-        if (mDeviceProductInfo) {
-            mDeviceProductInfo->dump(result);
-        } else {
-            result += "{}"s;
-        }
-    }
-
     result += "\n   powerMode="s;
     result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
     result += '\n';
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3eead17..d79a6b5 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -65,6 +65,10 @@
 class DisplaySurface;
 } // namespace compositionengine
 
+namespace display {
+class DisplaySnapshot;
+} // namespace display
+
 class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
@@ -80,11 +84,8 @@
         return mCompositionDisplay;
     }
 
-    std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; }
-
-    bool isVirtual() const { return !mConnectionType; }
+    bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); }
     bool isPrimary() const { return mIsPrimary; }
-    bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -94,7 +95,7 @@
     int getHeight() const;
     ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    void setLayerStack(ui::LayerStack);
+    void setLayerFilter(ui::LayerFilter);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
     void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
@@ -164,11 +165,6 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
-    const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
-        return mDeviceProductInfo;
-    }
-
     struct InputInfo {
         gui::DisplayInfo info;
         ui::Transform transform;
@@ -211,24 +207,14 @@
         return mUpcomingActiveMode;
     }
 
-    void setActiveMode(DisplayModeId) REQUIRES(kMainThreadContext);
+    // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
+    void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
+
     status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
                                 hal::VsyncPeriodChangeTimeline* outTimeline)
             REQUIRES(kMainThreadContext);
 
-    // Return the immutable list of supported display modes. The HWC may report different modes
-    // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
-    // Hotplug reconnects are common for external displays.
-    const DisplayModes& getSupportedModes() const;
-
-    // Returns nullptr if the given mode ID is not supported. A previously
-    // supported mode may be no longer supported for some devices like TVs and
-    // set-top boxes after a hotplug reconnect.
-    DisplayModePtr getMode(DisplayModeId) const;
-
-    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
-
     // Returns the refresh rate configs for this display.
     scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
 
@@ -267,7 +253,6 @@
     HWComposer& mHwComposer;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
-    const std::optional<ui::DisplayConnectionType> mConnectionType;
 
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
@@ -285,7 +270,6 @@
     DisplayModePtr mActiveMode;
     std::optional<float> mStagedBrightness = std::nullopt;
     float mBrightness = -1.f;
-    const DisplayModes mSupportedModes;
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
@@ -294,8 +278,6 @@
 
     uint32_t mFlags = 0;
 
-    std::optional<DeviceProductInfo> mDeviceProductInfo;
-
     std::vector<ui::Hdr> mOverrideHdrTypes;
 
     std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
@@ -313,14 +295,11 @@
 struct DisplayDeviceState {
     struct Physical {
         PhysicalDisplayId id;
-        ui::DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-        std::optional<DeviceProductInfo> deviceProductInfo;
-        DisplayModes supportedModes;
         DisplayModePtr activeMode;
 
         bool operator==(const Physical& other) const {
-            return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
+            return id == other.id && hwcDisplayId == other.hwcDisplayId;
         }
     };
 
@@ -356,7 +335,6 @@
     std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
 
     int32_t sequenceId{0};
-    std::optional<ui::DisplayConnectionType> connectionType;
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<compositionengine::DisplaySurface> displaySurface;
@@ -367,7 +345,6 @@
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
     std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
     bool isPrimary{false};
-    DisplayModes supportedModes;
     DisplayModeId activeModeId;
 };
 
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
deleted file mode 100644
index 7180fa6..0000000
--- a/services/surfaceflinger/EffectLayer.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "EffectLayer"
-
-#include "EffectLayer.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-EffectLayer::EffectLayer(const LayerCreationArgs& args) : BufferStateLayer(args) {}
-EffectLayer::~EffectLayer() = default;
-
-} // namespace android
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
deleted file mode 100644
index 311d493..0000000
--- a/services/surfaceflinger/EffectLayer.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 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 <sys/types.h>
-
-#include <cstdint>
-
-#include "BufferStateLayer.h"
-
-namespace android {
-
-// A layer that can render a combination of the following effects.
-//   * fill the bounds of the layer with a color
-//   * render a shadow cast by the bounds of the layer
-// If no effects are enabled, the layer is considered to be invisible.
-class EffectLayer : public BufferStateLayer {
-public:
-    explicit EffectLayer(const LayerCreationArgs&);
-    ~EffectLayer() override;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 47bd91e..08b71c2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -65,9 +65,9 @@
 #include <mutex>
 #include <sstream>
 
+#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
-#include "EffectLayer.h"
 #include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
@@ -465,6 +465,10 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
     }
+
+    if (mPotentialCursor) {
+        prepareCursorCompositionState();
+    }
 }
 
 Rect Layer::getCroppedBufferSize(const State& s) const {
@@ -650,30 +654,6 @@
     return sp<compositionengine::LayerFE>::fromExisting(layerFE);
 }
 
-void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
-    using StateSubset = compositionengine::LayerFE::StateSubset;
-
-    switch (subset) {
-        case StateSubset::BasicGeometry:
-            prepareBasicGeometryCompositionState();
-            break;
-
-        case StateSubset::GeometryAndContent:
-            prepareBasicGeometryCompositionState();
-            prepareGeometryCompositionState();
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Content:
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Cursor:
-            prepareCursorCompositionState();
-            break;
-    }
-}
-
 const char* Layer::getDebugName() const {
     return mName.c_str();
 }
@@ -2205,8 +2185,8 @@
     renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
 
     // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if
-    // transparent regions are present. This may not be necessary since shadows are only cast by
-    // SurfaceFlinger's EffectLayers, which do not typically use transparent regions.
+    // transparent regions are present. This may not be necessary since shadows are typically cast
+    // by layers without transparent regions.
     state.boundaries = mBounds;
 
     // Shift the spot light x-position to the middle of the display and then
@@ -3426,7 +3406,7 @@
     return fenceSignaled;
 }
 
-bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+bool Layer::onPreComposition(nsecs_t refreshStartTime, bool /* updatingOutputGeometryThisFrame */) {
     for (const auto& handle : mDrawingState.callbackHandles) {
         handle->refreshStartTime = refreshStartTime;
     }
@@ -3947,7 +3927,7 @@
     }
 
     if (display) {
-        const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+        const Fps refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
 
@@ -4240,6 +4220,18 @@
     return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
 }
 
+void Layer::updateSnapshot(bool updateGeometry) {
+    if (!getCompositionEngineLayerFE()) {
+        return;
+    }
+
+    if (updateGeometry) {
+        prepareBasicGeometryCompositionState();
+        prepareGeometryCompositionState();
+    }
+    preparePerFrameCompositionState();
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f6b9b0f..5030fd8 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -574,9 +574,8 @@
     // implements compositionengine::LayerFE
     const compositionengine::LayerFECompositionState* getCompositionState() const;
     bool fenceHasSignaled() const;
-    bool onPreComposition(nsecs_t);
-    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
-
+    // Called before composition. updatingOutputGeometryThisFrame is used by ARC++'s Layer subclass.
+    bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame);
     std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) const override;
     void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
@@ -868,6 +867,7 @@
     bool simpleBufferUpdate(const layer_state_t&) const;
 
     static bool isOpaqueFormat(PixelFormat format);
+    void updateSnapshot(bool updateGeometry);
 
 protected:
     friend class impl::SurfaceInterceptor;
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index e17b01f..6bc7dc1 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -18,7 +18,6 @@
 #include <ui/Transform.h>
 
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "LayerRenderArea.h"
 #include "SurfaceFlinger.h"
@@ -110,7 +109,7 @@
         // layer which has no properties set and which does not draw.
         //  We hold the statelock as the reparent-for-drawing operation modifies the
         //  hierarchy and there could be readers on Binder threads, like dump.
-        sp<EffectLayer> screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
+        auto screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
                 {&mFlinger, nullptr, "Screenshot Parent"s, ISurfaceComposerClient::eNoColorFill,
                  LayerMetadata()});
         {
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 747032b..4af1f5c 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -188,10 +188,10 @@
 
 VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const {
     std::lock_guard lock(mVsyncMutex);
-    nsecs_t expectedPresentTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
+    nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
             systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
-    nsecs_t deadline = expectedPresentTime - mWorkDuration.get().count() - mReadyDuration.count();
-    return {expectedPresentTime, deadline};
+    nsecs_t deadline = expectedPresentationTime - mReadyDuration.count();
+    return {expectedPresentationTime, deadline};
 }
 
 void DispSyncSource::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index a48c921..00886f0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -27,6 +27,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
+#include <ftl/fake_guard.h>
 #include <utils/Trace.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -279,6 +280,15 @@
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
+    const auto& activeMode = *getActiveModeItLocked()->second;
+
+    // Keep the display at max refresh rate for the duration of powering on the display.
+    if (signals.powerOnImminent) {
+        ALOGV("Power On Imminent");
+        const auto& max = getMaxRefreshRateByPolicyLocked(activeMode.getGroup());
+        return {max, GlobalSignals{.powerOnImminent = true}};
+    }
+
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -325,6 +335,7 @@
 
     const Policy* policy = getCurrentPolicyLocked();
     const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
     // If the default mode group is different from the group of current mode,
     // this means a layer requesting a seamed mode switch just disappeared and
     // we should switch back to the default group.
@@ -332,7 +343,7 @@
     // of the current mode, in order to prevent unnecessary seamed mode switches
     // (e.g. when pausing a video playback).
     const auto anchorGroup =
-            seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup();
+            seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup();
 
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
@@ -387,12 +398,12 @@
 
         for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
             const auto& [id, mode] = *modeIt;
-            const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
+            const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -401,7 +412,7 @@
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -413,7 +424,7 @@
             const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str());
+                      to_string(*mode).c_str(), to_string(activeMode).c_str());
                 continue;
             }
 
@@ -676,7 +687,7 @@
 
     const DisplayModePtr& current = desiredActiveModeId
             ? mDisplayModes.get(*desiredActiveModeId)->get()
-            : mActiveModeIt->second;
+            : getActiveModeItLocked()->second;
 
     const DisplayModePtr& min = mMinRefreshRateModeIt->second;
     if (current == min) {
@@ -688,16 +699,17 @@
 }
 
 const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    const auto& activeMode = *getActiveModeItLocked()->second;
+
     for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
         const auto& mode = modeIt->second;
-        if (mActiveModeIt->second->getGroup() == mode->getGroup()) {
+        if (activeMode.getGroup() == mode->getGroup()) {
             return mode;
         }
     }
 
-    ALOGE("Can't find min refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
+    ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s",
+          to_string(activeMode).c_str());
 
     // Default to the lowest refresh rate.
     return mPrimaryRefreshRates.front()->second;
@@ -708,6 +720,11 @@
     return getMaxRefreshRateByPolicyLocked();
 }
 
+const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+    const int anchorGroup = getActiveModeItLocked()->second->getGroup();
+    return getMaxRefreshRateByPolicyLocked(anchorGroup);
+}
+
 const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
     for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
         const auto& mode = (*it)->second;
@@ -716,17 +733,28 @@
         }
     }
 
-    ALOGE("Can't find max refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
+    const auto& activeMode = *getActiveModeItLocked()->second;
+    ALOGE("Can't find max refresh rate by policy with the same mode group as the current mode %s",
+          to_string(activeMode).c_str());
 
     // Default to the highest refresh rate.
     return mPrimaryRefreshRates.back()->second;
 }
 
-DisplayModePtr RefreshRateConfigs::getActiveMode() const {
+DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
     std::lock_guard lock(mLock);
-    return mActiveModeIt->second;
+    return getActiveModeItLocked()->second;
+}
+
+const DisplayMode& RefreshRateConfigs::getActiveMode() const {
+    // Reads from kMainThreadContext do not require mLock.
+    ftl::FakeGuard guard(mLock);
+    return *mActiveModeIt->second;
+}
+
+DisplayModeIterator RefreshRateConfigs::getActiveModeItLocked() const {
+    // Reads under mLock do not require kMainThreadContext.
+    return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
 }
 
 void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
@@ -744,7 +772,7 @@
                                        Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     initializeIdleTimer();
-    updateDisplayModes(std::move(modes), activeModeId);
+    FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
 }
 
 void RefreshRateConfigs::initializeIdleTimer() {
@@ -976,7 +1004,7 @@
 
     std::lock_guard lock(mLock);
 
-    const auto activeModeId = mActiveModeIt->first;
+    const auto activeModeId = getActiveModeItLocked()->first;
     result += "   activeModeId="s;
     result += std::to_string(activeModeId.value());
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index a79002e..19bcb94 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -31,6 +31,7 @@
 #include "DisplayHardware/HWComposer.h"
 #include "Scheduler/OneShotTimer.h"
 #include "Scheduler/StrongTyping.h"
+#include "ThreadContext.h"
 
 namespace android::scheduler {
 
@@ -184,9 +185,13 @@
         bool touch = false;
         // True if the system hasn't seen any buffers posted to layers recently.
         bool idle = false;
+        // Whether the display is about to be powered on, or has been in PowerMode::ON
+        // within the timeout of DisplayPowerTimer.
+        bool powerOnImminent = false;
 
         bool operator==(GlobalSignals other) const {
-            return touch == other.touch && idle == other.idle;
+            return touch == other.touch && idle == other.idle &&
+                    powerOnImminent == other.powerOnImminent;
         }
     };
 
@@ -207,8 +212,11 @@
     // uses the primary range, not the app request range.
     DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
 
-    void setActiveModeId(DisplayModeId) EXCLUDES(mLock);
-    DisplayModePtr getActiveMode() const EXCLUDES(mLock);
+    void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+    // See mActiveModeIt for thread safety.
+    DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
+    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
 
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -332,6 +340,9 @@
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
+    // See mActiveModeIt for thread safety.
+    DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+
     std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
             const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
 
@@ -345,10 +356,8 @@
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
+    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
     const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
-    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
-        return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup());
-    }
 
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
@@ -361,7 +370,8 @@
     float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
             REQUIRES(mLock);
 
-    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock);
+    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
+            REQUIRES(kMainThreadContext);
 
     void initializeIdleTimer();
 
@@ -377,7 +387,10 @@
     // is also dependent, so must be reset as well.
     DisplayModes mDisplayModes GUARDED_BY(mLock);
 
-    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock);
+    // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
+    // need not be under mLock.
+    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+
     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 55ae013..ff6b461 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -182,7 +182,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
+        const Fps refreshRate = holdRefreshRateConfigs()->getActiveModePtr()->getFps();
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -320,7 +320,7 @@
     // 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 (std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->getActiveMode() != mPolicy.mode) {
+        mRefreshRateConfigs->getActiveModePtr() != mPolicy.mode) {
         return;
     }
 
@@ -453,7 +453,7 @@
     if (now - last > kIgnoreDelay) {
         const auto refreshRate = [&] {
             std::scoped_lock lock(mRefreshRateConfigsLock);
-            return mRefreshRateConfigs->getActiveMode()->getFps();
+            return mRefreshRateConfigs->getActiveModePtr()->getFps();
         }();
         resyncToHardwareVsync(false, refreshRate);
     }
@@ -577,7 +577,7 @@
     // magic number
     const Fps refreshRate = [&] {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps();
+        return mRefreshRateConfigs->getActiveModePtr()->getFps();
     }();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
@@ -704,17 +704,13 @@
 
     const auto configs = holdRefreshRateConfigs();
 
-    // If Display Power is not in normal operation we want to be in performance mode. When coming
-    // back to normal mode, a grace period is given with DisplayPowerTimer.
-    if (mDisplayPowerTimer &&
-        (mPolicy.displayPowerMode != hal::PowerMode::ON ||
-         mPolicy.displayPowerTimer == TimerState::Reset)) {
-        constexpr GlobalSignals kNoSignals;
-        return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
-    }
+    const bool powerOnImminent = mDisplayPowerTimer &&
+            (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+             mPolicy.displayPowerTimer == TimerState::Reset);
 
     const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
-                                .idle = mPolicy.idleTimer == TimerState::Expired};
+                                .idle = mPolicy.idleTimer == TimerState::Expired,
+                                .powerOnImminent = powerOnImminent};
 
     return configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index eb3856d..afb3459 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -229,7 +229,7 @@
 
     nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps().getPeriodNsecs();
+        return mRefreshRateConfigs->getActiveModePtr()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cc93db3..e31490c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -53,9 +53,10 @@
 #include <configstore/Utils.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
+#include <ftl/algorithm.h>
 #include <ftl/fake_guard.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
+#include <ftl/unit.h>
 #include <gui/AidlStatusUtil.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
@@ -108,6 +109,7 @@
 #include "BufferStateLayer.h"
 #include "Client.h"
 #include "Colorizer.h"
+#include "Display/DisplayMap.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/FramebufferSurface.h"
@@ -116,7 +118,6 @@
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
 #include "FpsReporter.h"
@@ -172,6 +173,8 @@
         OutputCompositionState::CompositionStrategyPredictionState;
 
 using base::StringAppendF;
+using display::PhysicalDisplay;
+using display::PhysicalDisplays;
 using gui::DisplayInfo;
 using gui::GameMode;
 using gui::IDisplayEventConnection;
@@ -594,12 +597,12 @@
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
     std::vector<PhysicalDisplayId> displayIds;
-    displayIds.reserve(mPhysicalDisplayTokens.size());
+    displayIds.reserve(mPhysicalDisplays.size());
 
     const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId();
     displayIds.push_back(defaultDisplayId);
 
-    for (const auto& [id, token] : mPhysicalDisplayTokens) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
         if (id != defaultDisplayId) {
             displayIds.push_back(id);
         }
@@ -608,6 +611,12 @@
     return displayIds;
 }
 
+std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked(
+        const sp<display::DisplayToken>& displayToken) const {
+    return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+            .transform(&ftl::to_key<PhysicalDisplays>);
+}
+
 sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
     Mutex::Autolock lock(mStateLock);
     return getPhysicalDisplayTokenLocked(displayId);
@@ -932,16 +941,19 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
+    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;
     }
 
-    if (const auto connectionType = display->getConnectionType())
-        info->connectionType = *connectionType;
-    else {
-        return INVALID_OPERATION;
-    }
+    const auto& [display, snapshotRef] = *displayOpt;
+    const auto& snapshot = snapshotRef.get();
+
+    info->connectionType = snapshot.connectionType();
+    info->deviceProductInfo = snapshot.deviceProductInfo();
 
     if (mEmulatedDisplayDensity) {
         info->density = mEmulatedDisplayDensity;
@@ -953,7 +965,6 @@
     info->density /= ACONFIGURATION_DENSITY_MEDIUM;
 
     info->secure = display->isSecure();
-    info->deviceProductInfo = display->getDeviceProductInfo();
     info->installOrientation = display->getPhysicalOrientation();
 
     return NO_ERROR;
@@ -967,23 +978,22 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
+    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 displayId = PhysicalDisplayId::tryCast(display->getId());
-    if (!displayId) {
-        return INVALID_OPERATION;
-    }
+    const auto& [display, snapshotRef] = *displayOpt;
+    const auto& snapshot = snapshotRef.get();
 
-    info->activeDisplayModeId = display->getActiveMode()->getId().value();
+    const auto& displayModes = snapshot.displayModes();
 
-    const auto& supportedModes = display->getSupportedModes();
     info->supportedDisplayModes.clear();
-    info->supportedDisplayModes.reserve(supportedModes.size());
+    info->supportedDisplayModes.reserve(displayModes.size());
 
-    for (const auto& [id, mode] : supportedModes) {
+    for (const auto& [id, mode] : displayModes) {
         ui::DisplayMode outMode;
         outMode.id = static_cast<int32_t>(id.value());
 
@@ -1027,21 +1037,24 @@
         info->supportedDisplayModes.push_back(outMode);
     }
 
+    const PhysicalDisplayId displayId = snapshot.displayId();
+
+    info->activeDisplayModeId = display->getActiveMode()->getId().value();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    info->supportedColorModes = getDisplayColorModes(*display);
+    info->supportedColorModes = getDisplayColorModes(displayId);
     info->hdrCapabilities = display->getHdrCapabilities();
 
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(*displayId,
+            getHwComposer().hasDisplayCapability(displayId,
                                                  DisplayCapability::AUTO_LOW_LATENCY_MODE);
     info->gameContentTypeSupported =
-            getHwComposer().supportsContentType(*displayId, hal::ContentType::GAME);
+            getHwComposer().supportsContentType(displayId, hal::ContentType::GAME);
 
     info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1);
 
     if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) {
-        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(*displayId)) {
-            if (const auto modeId = display->translateModeId(*hwcId)) {
+        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) {
+            if (const auto modeId = snapshot.translateModeId(*hwcId)) {
                 info->preferredBootDisplayMode = modeId->value();
             }
         }
@@ -1089,39 +1102,44 @@
     }
 }
 
-status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
+                                                   DisplayModeId modeId) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
+    const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() -> status_t {
-        const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
-        if (!display) {
-            ALOGE("Attempt to set allowed display modes for invalid display token %p",
-                  displayToken.get());
+        const auto displayOpt =
+                FTL_FAKE_GUARD(mStateLock,
+                               ftl::find_if(mPhysicalDisplays,
+                                            PhysicalDisplay::hasToken(displayToken))
+                                       .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                       .and_then(getDisplayDeviceAndSnapshot()));
+        if (!displayOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display modes for virtual display");
-            return INVALID_OPERATION;
-        }
+        const auto& [display, snapshotRef] = *displayOpt;
+        const auto& snapshot = snapshotRef.get();
 
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGW("Attempt to switch to an unsupported mode %d.", modeId);
+        const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getFps(); });
+
+        if (!fpsOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        const auto fps = mode->getFps();
+        const Fps fps = *fpsOpt;
         // Keep the old switching type.
-        const auto allowGroupSwitching =
+        const bool allowGroupSwitching =
                 display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
-        const scheduler::RefreshRateConfigs::Policy policy{mode->getId(),
-                                                           allowGroupSwitching,
-                                                           {fps, fps}};
+        const scheduler::RefreshRateConfigs::Policy policy{modeId, allowGroupSwitching, {fps, fps}};
         constexpr bool kOverridePolicy = false;
 
         return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
@@ -1138,9 +1156,7 @@
         return;
     }
 
-    const auto upcomingModeInfo =
-            FTL_FAKE_GUARD(kMainThreadContext, display->getUpcomingActiveMode());
-
+    const auto upcomingModeInfo = display->getUpcomingActiveMode();
     if (!upcomingModeInfo.mode) {
         // There is no pending mode change. This can happen if the active
         // display changed and the mode change happened on a different display.
@@ -1159,9 +1175,12 @@
         return;
     }
 
-    // We just created this display so we can call even if we are not on the main thread.
-    ftl::FakeGuard guard(kMainThreadContext);
-    display->setActiveMode(upcomingModeInfo.mode->getId());
+    mPhysicalDisplays.get(display->getPhysicalId())
+            .transform(&PhysicalDisplay::snapshotRef)
+            .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                FTL_FAKE_GUARD(kMainThreadContext,
+                               display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot));
+            }));
 
     const Fps refreshRate = upcomingModeInfo.mode->getFps();
     mRefreshRateStats->setRefreshRate(refreshRate);
@@ -1191,12 +1210,16 @@
 
     std::optional<PhysicalDisplayId> displayToUpdateImmediately;
 
-    for (const auto& iter : mDisplays) {
-        const auto& display = iter.second;
-        if (!display || !display->isInternal()) {
+    for (const auto& [id, physical] : mPhysicalDisplays) {
+        const auto& snapshot = physical.snapshot();
+
+        if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) {
             continue;
         }
 
+        const auto display = getDisplayDeviceLocked(id);
+        if (!display) continue;
+
         // Store the local variable to release the lock.
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (!desiredActiveMode) {
@@ -1210,20 +1233,23 @@
             continue;
         }
 
-        const auto desiredMode = display->getMode(desiredActiveMode->mode->getId());
-        if (!desiredMode) {
+        const auto desiredModeId = desiredActiveMode->mode->getId();
+        const auto refreshRateOpt =
+                snapshot.displayModes()
+                        .get(desiredModeId)
+                        .transform([](const DisplayModePtr& mode) { return mode->getFps(); });
+
+        if (!refreshRateOpt) {
             ALOGW("Desired display mode is no longer supported. Mode ID = %d",
-                  desiredActiveMode->mode->getId().value());
+                  desiredModeId.value());
             clearDesiredActiveModeState(display);
             continue;
         }
 
-        const auto refreshRate = desiredMode->getFps();
-        ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
-              desiredMode->getId().value(), to_string(refreshRate).c_str(),
-              to_string(display->getId()).c_str());
+        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());
 
-        if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+        if (display->getActiveMode()->getId() == desiredModeId) {
             // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
@@ -1232,8 +1258,7 @@
         // 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->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
+        const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredModeId);
         if (!displayModeAllowed) {
             clearDesiredActiveModeState(display);
             continue;
@@ -1245,9 +1270,8 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        const auto status = FTL_FAKE_GUARD(kMainThreadContext,
-                                           display->initiateModeChange(*desiredActiveMode,
-                                                                       constraints, &outTimeline));
+        const auto status =
+                display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline);
 
         if (status != NO_ERROR) {
             // initiateModeChange may fail if a hotplug event is just about
@@ -1295,15 +1319,18 @@
     future.wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
-    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
+    auto modes = getHwComposer().getColorModes(displayId);
+
+    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                           .transform(&PhysicalDisplay::isInternal)
+                                           .value_or(false);
 
     // If the display is internal and the configuration claims it's not wide color capable,
     // filter out all wide color modes. The typical reason why this happens is that the
     // hardware is not good enough to support GPU composition of wide color, and thus the
     // OEMs choose to disable this capability.
-    if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
-        !hasWideColorDisplay) {
+    if (isInternalDisplay && !hasWideColorDisplay) {
         const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
         modes.erase(newEnd, modes.end());
     }
@@ -1319,13 +1346,13 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
+    const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                 .transform(&ftl::to_mapped_ref<PhysicalDisplays>);
     if (!display) {
         return NAME_NOT_FOUND;
     }
 
-    const auto connectionType = display->getConnectionType();
-    if (connectionType != ui::DisplayConnectionType::Internal) {
+    if (!display.transform(&PhysicalDisplay::isInternal).value()) {
         return INVALID_OPERATION;
     }
 
@@ -1353,7 +1380,7 @@
             return INVALID_OPERATION;
         }
 
-        const auto modes = getDisplayColorModes(*display);
+        const auto modes = getDisplayColorModes(display->getPhysicalId());
         const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
 
         if (mode < ColorMode::NATIVE || !exists) {
@@ -1381,30 +1408,31 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken,
-                                            ui::DisplayModeId modeId) {
+status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
+                                            DisplayModeId modeId) {
     const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+        const auto snapshotOpt =
+                ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                        .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                        .transform(&PhysicalDisplay::snapshotRef);
+
+        if (!snapshotOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGE("%s: Invalid operation on virtual display", whence);
-            return INVALID_OPERATION;
-        }
+        const auto& snapshot = snapshotOpt->get();
+        const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getHwcId(); });
 
-        const auto displayId = display->getPhysicalId();
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGE("%s: Invalid mode %d for display %s", whence, modeId,
-                  to_string(displayId).c_str());
+        if (!hwcIdOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        return getHwComposer().setBootDisplayMode(displayId, mode->getHwcId());
+        return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt);
     });
     return future.get();
 }
@@ -2129,7 +2157,7 @@
     // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
     // and may eventually call to ~Layer() if it holds the last reference
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
         mScheduler->chooseRefreshRateForContent();
         setActiveModeInHwcIfNeeded();
     }
@@ -2161,10 +2189,6 @@
         displayIds.push_back(display->getId());
     }
     mPowerAdvisor->setDisplays(displayIds);
-    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
-        if (auto layerFE = layer->getCompositionEngineLayerFE())
-            refreshArgs.layers.push_back(layerFE);
-    });
 
     if (DOES_CONTAIN_BORDER) {
         refreshArgs.borderInfoList.clear();
@@ -2195,6 +2219,12 @@
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
+    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
+        layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
+        if (auto layerFE = layer->getCompositionEngineLayerFE()) {
+            refreshArgs.layers.push_back(layerFE);
+        }
+    });
     refreshArgs.blursAreExpensive = mBlursAreExpensive;
     refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
 
@@ -2467,7 +2497,13 @@
     mTimeStats->incrementTotalFrames();
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
-    if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
+    const bool isInternalDisplay = display &&
+            FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)
+                    .get(display->getPhysicalId())
+                    .transform(&PhysicalDisplay::isInternal)
+                    .value_or(false);
+
+    if (isInternalDisplay && display && display->getPowerMode() == hal::PowerMode::ON &&
         presentFenceTime->isValid()) {
         mScheduler->addPresentFence(std::move(presentFenceTime));
     }
@@ -2601,10 +2637,11 @@
         return {};
     }
 
-    DisplayModes oldModes;
-    if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
-        oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
-    }
+    const DisplayModes oldModes = mPhysicalDisplays.get(displayId)
+                                          .transform([](const PhysicalDisplay& display) {
+                                              return display.snapshot().displayModes();
+                                          })
+                                          .value_or(DisplayModes{});
 
     ui::DisplayModeId nextModeId = 1 +
             std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1),
@@ -2669,21 +2706,22 @@
 const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId,
                                            hal::HWDisplayId hwcDisplayId, bool connected,
                                            DisplayIdentificationInfo&& info) {
-    const auto tokenOpt = mPhysicalDisplayTokens.get(displayId);
+    const auto displayOpt = mPhysicalDisplays.get(displayId);
     if (!connected) {
-        LOG_ALWAYS_FATAL_IF(!tokenOpt);
+        LOG_ALWAYS_FATAL_IF(!displayOpt);
+        const auto& display = displayOpt->get();
 
-        if (const ssize_t index = mCurrentState.displays.indexOfKey(tokenOpt->get()); index >= 0) {
+        if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
             const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
             mInterceptor->saveDisplayDeletion(state.sequenceId);
             mCurrentState.displays.removeItemsAt(index);
         }
 
-        mPhysicalDisplayTokens.erase(displayId);
+        mPhysicalDisplays.erase(displayId);
         return "Disconnecting";
     }
 
-    auto [supportedModes, activeMode] = loadDisplayModes(displayId);
+    auto [displayModes, activeMode] = loadDisplayModes(displayId);
     if (!activeMode) {
         // TODO(b/241286153): Report hotplug failure to the framework.
         ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
@@ -2691,30 +2729,42 @@
         return nullptr;
     }
 
-    if (tokenOpt) {
-        auto& state = mCurrentState.displays.editValueFor(tokenOpt->get());
-        state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
-        state.physical->supportedModes = std::move(supportedModes);
-        state.physical->activeMode = std::move(activeMode);
+    if (displayOpt) {
+        const auto& display = displayOpt->get();
+        const auto& snapshot = display.snapshot();
+
+        std::optional<DeviceProductInfo> deviceProductInfo;
         if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
-            state.physical->deviceProductInfo = std::move(info.deviceProductInfo);
+            deviceProductInfo = std::move(info.deviceProductInfo);
+        } else {
+            deviceProductInfo = snapshot.deviceProductInfo();
         }
+
+        const auto it =
+                mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
+                                              snapshot.connectionType(), std::move(displayModes),
+                                              std::move(deviceProductInfo));
+
+        auto& state = mCurrentState.displays.editValueFor(it->second.token());
+        state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
+        state.physical->activeMode = std::move(activeMode);
         return "Reconnecting";
     }
 
+    const sp<IBinder> token = sp<BBinder>::make();
+
+    mPhysicalDisplays.try_emplace(displayId, token, displayId,
+                                  getHwComposer().getDisplayConnectionType(displayId),
+                                  std::move(displayModes), std::move(info.deviceProductInfo));
+
     DisplayDeviceState state;
     state.physical = {.id = displayId,
-                      .type = getHwComposer().getDisplayConnectionType(displayId),
                       .hwcDisplayId = hwcDisplayId,
-                      .deviceProductInfo = std::move(info.deviceProductInfo),
-                      .supportedModes = std::move(supportedModes),
                       .activeMode = std::move(activeMode)};
     state.isSecure = true; // All physical displays are currently considered secure.
     state.displayName = std::move(info.name);
 
-    sp<IBinder> token = sp<BBinder>::make();
     mCurrentState.displays.add(token, state);
-    mPhysicalDisplayTokens.try_emplace(displayId, std::move(token));
     mInterceptor->saveDisplayCreation(state);
     return "Connecting";
 }
@@ -2739,8 +2789,6 @@
     creationArgs.supportedPerFrameMetadata = 0;
 
     if (const auto& physical = state.physical) {
-        creationArgs.connectionType = physical->type;
-        creationArgs.supportedModes = physical->supportedModes;
         creationArgs.activeModeId = physical->activeMode->getId();
         const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                 getKernelIdleTimerProperties(compositionDisplay->getId());
@@ -2751,9 +2799,17 @@
                          base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
                  .idleTimerTimeout = idleTimerTimeoutMs,
                  .kernelIdleTimerController = kernelIdleTimerController};
+
         creationArgs.refreshRateConfigs =
-                std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
-                                                                creationArgs.activeModeId, config);
+                mPhysicalDisplays.get(physical->id)
+                        .transform(&PhysicalDisplay::snapshotRef)
+                        .transform([&](const display::DisplaySnapshot& snapshot) {
+                            return std::make_shared<
+                                    scheduler::RefreshRateConfigs>(snapshot.displayModes(),
+                                                                   creationArgs.activeModeId,
+                                                                   config);
+                        })
+                        .value_or(nullptr);
     }
 
     if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
@@ -2811,13 +2867,17 @@
             compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
-    if (!state.isVirtual()) {
-        FTL_FAKE_GUARD(kMainThreadContext,
-                       display->setActiveMode(state.physical->activeMode->getId()));
-        display->setDeviceProductInfo(state.physical->deviceProductInfo);
+
+    if (const auto& physical = state.physical) {
+        mPhysicalDisplays.get(physical->id)
+                .transform(&PhysicalDisplay::snapshotRef)
+                .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                    FTL_FAKE_GUARD(kMainThreadContext,
+                                   display->setActiveMode(physical->activeMode->getId(), snapshot));
+                }));
     }
 
-    display->setLayerStack(state.layerStack);
+    display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
@@ -2973,7 +3033,8 @@
 
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
         if (currentState.layerStack != drawingState.layerStack) {
-            display->setLayerStack(currentState.layerStack);
+            display->setLayerFilter(
+                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
         }
         if (currentState.flags != drawingState.flags) {
             display->setFlags(currentState.flags);
@@ -2997,9 +3058,10 @@
         }
     }
 }
+
 void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode()->getFps();
+    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode().getFps();
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3219,7 +3281,7 @@
 
 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                       std::vector<DisplayInfo>& outDisplayInfos) {
-    ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos;
+    display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos;
 
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
         const auto layerStack = display->getLayerStack();
@@ -4661,8 +4723,6 @@
     }
 }
 
-// ---------------------------------------------------------------------------
-
 void SurfaceFlinger::onInitializeDisplays() {
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display) return;
@@ -4700,8 +4760,9 @@
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    static_cast<void>(
-            mScheduler->schedule([this]() FTL_FAKE_GUARD(mStateLock) { onInitializeDisplays(); }));
+    static_cast<void>(mScheduler->schedule(
+            [this]() FTL_FAKE_GUARD(mStateLock)
+                    FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4718,8 +4779,12 @@
         return;
     }
 
+    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                           .transform(&PhysicalDisplay::isInternal)
+                                           .value_or(false);
+
     const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
-    if (activeDisplay != display && display->isInternal() && activeDisplay &&
+    if (isInternalDisplay && activeDisplay != display && activeDisplay &&
         activeDisplay->isPoweredOn()) {
         ALOGW("Trying to change power mode on non active display while the active display is ON");
     }
@@ -4729,10 +4794,10 @@
     if (mInterceptor->isEnabled()) {
         mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
-    const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+    const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps();
     if (*currentMode == hal::PowerMode::OFF) {
         // Turn on the display
-        if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+        if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
             onActiveDisplayChangedLocked(display);
         }
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
@@ -4803,7 +4868,8 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+                                               kMainThreadContext) {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4991,10 +5057,20 @@
 }
 
 void SurfaceFlinger::dumpDisplays(std::string& result) const {
-    for (const auto& [token, display] : mDisplays) {
-        display->dump(result);
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (const auto device = getDisplayDeviceLocked(id)) {
+            device->dump(result);
+        }
+        display.snapshot().dump(result);
         result += '\n';
     }
+
+    for (const auto& [token, display] : mDisplays) {
+        if (display->isVirtual()) {
+            display->dump(result);
+            result += '\n';
+        }
+    }
 }
 
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
@@ -5793,7 +5869,7 @@
                 }();
 
                 mDebugDisplayModeSetByBackdoor = false;
-                const status_t result = setActiveModeFromBackdoor(display, modeId);
+                const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
                 mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
                 return result;
             }
@@ -6667,6 +6743,20 @@
     }
 }
 
+std::optional<DisplayModePtr> SurfaceFlinger::getPreferredDisplayMode(
+        PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
+    if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
+        schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
+        return schedulerMode;
+    }
+
+    return mPhysicalDisplays.get(displayId)
+            .transform(&PhysicalDisplay::snapshotRef)
+            .and_then([&](const display::DisplaySnapshot& snapshot) {
+                return snapshot.displayModes().get(defaultModeId);
+            });
+}
+
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
         const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
@@ -6685,7 +6775,7 @@
         return NO_ERROR;
     }
 
-    scheduler::RefreshRateConfigs::Policy currentPolicy =
+    const scheduler::RefreshRateConfigs::Policy currentPolicy =
             display->refreshRateConfigs().getCurrentPolicy();
 
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
@@ -6700,27 +6790,25 @@
         mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
     }
 
-    const DisplayModePtr preferredDisplayMode = [&] {
-        const auto schedulerMode = mScheduler->getPreferredDisplayMode();
-        if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) {
-            return schedulerMode;
-        }
-
-        return display->getMode(currentPolicy.defaultMode);
-    }();
-
-    ALOGV("trying to switch to Scheduler preferred mode %d (%s)",
-          preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str());
-
-    if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
-        ALOGV("switching to Scheduler preferred display mode %d",
-              preferredDisplayMode->getId().value());
-        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
-    } else {
-        LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
-                         preferredDisplayMode->getId().value());
+    auto preferredModeOpt =
+            getPreferredDisplayMode(display->getPhysicalId(), currentPolicy.defaultMode);
+    if (!preferredModeOpt) {
+        ALOGE("%s: Preferred mode is unknown", __func__);
+        return NAME_NOT_FOUND;
     }
 
+    auto preferredMode = std::move(*preferredModeOpt);
+    const auto preferredModeId = preferredMode->getId();
+
+    ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
+          to_string(preferredMode->getFps()).c_str());
+
+    if (!display->refreshRateConfigs().isModeAllowed(preferredModeId)) {
+        ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
+        return INVALID_OPERATION;
+    }
+
+    setDesiredActiveMode({std::move(preferredMode), DisplayModeEvent::Changed});
     return NO_ERROR;
 }
 
@@ -6879,9 +6967,11 @@
 }
 
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
-    for (const auto& [ignored, display] : mDisplays) {
-        if (display->isInternal()) {
-            display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+            if (const auto device = getDisplayDeviceLocked(id)) {
+                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+            }
         }
     }
 }
@@ -6930,7 +7020,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+            refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
         }
     }
 
@@ -7367,8 +7457,7 @@
                                                        int displayModeId) {
     status_t status = checkAccessPermission();
     if (status == OK) {
-        status = mFlinger->setBootDisplayMode(display,
-                                              static_cast<ui::DisplayModeId>(displayModeId));
+        status = mFlinger->setBootDisplayMode(display, DisplayModeId{displayModeId});
     }
     return binderStatusFromStatusT(status);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3101885..f7684a0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,7 +29,6 @@
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
 #include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
@@ -59,6 +58,8 @@
 #include <ui/FenceResult.h>
 
 #include "ClientCache.h"
+#include "Display/DisplayMap.h"
+#include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
@@ -515,7 +516,7 @@
     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;
-    status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id);
+    status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
     status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
@@ -647,34 +648,39 @@
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
 
-    // Called on the main thread in response to initializeDisplays()
-    void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
     void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
-    status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+    status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
     // Sets the active mode and a new refresh rate in SF.
-    void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
+    void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
     // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
-    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
+    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext);
     void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called when active mode is no longer is progress
     void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
-            REQUIRES(mStateLock);
+            REQUIRES(mStateLock, kMainThreadContext);
 
     // Returns true if the display has a visible HDR layer in its layer stack.
     bool hasVisibleHdrLayer(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
 
+    // 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<DisplayModePtr> getPreferredDisplayMode(PhysicalDisplayId,
+                                                          DisplayModeId defaultModeId) const
+            REQUIRES(mStateLock);
+
     // Sets the desired display mode specs.
     status_t setDesiredDisplayModeSpecsInternal(
             const sp<DisplayDevice>& display,
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
             EXCLUDES(mStateLock);
 
-    void commitTransactions() EXCLUDES(mStateLock);
-    void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+    void commitTransactionsLocked(uint32_t transactionFlags)
+            REQUIRES(mStateLock, kMainThreadContext);
     void doCommitTransactions() REQUIRES(mStateLock);
 
     // Returns whether a new buffer has been latched.
@@ -817,8 +823,14 @@
     /*
      * Display and layer stack management
      */
-    // called when starting, or restarting after system_server death
+
+    // Called during boot, and restart after system_server death.
     void initializeDisplays();
+    void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
+
+    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
+        return display->getDisplayToken() == mActiveDisplayToken;
+    }
 
     sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
             REQUIRES(mStateLock) {
@@ -867,6 +879,21 @@
         return getDefaultDisplayDeviceLocked();
     }
 
+    using DisplayDeviceAndSnapshot =
+            std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>;
+
+    // Combinator for ftl::Optional<PhysicalDisplay>::and_then.
+    auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) {
+        return [this](const display::PhysicalDisplay& display) REQUIRES(
+                       mStateLock) -> ftl::Optional<DisplayDeviceAndSnapshot> {
+            if (auto device = getDisplayDeviceLocked(display.snapshot().displayId())) {
+                return std::make_pair(std::move(device), display.snapshotRef());
+            }
+
+            return {};
+        };
+    }
+
     // Returns the first display that matches a `bool(const DisplayDevice&)` predicate.
     template <typename Predicate>
     sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
@@ -882,8 +909,13 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
-    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
-        return display->getDisplayToken() == mActiveDisplayToken;
+    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+            REQUIRES(mStateLock) {
+        return {layerStack,
+                PhysicalDisplayId::tryCast(displayId)
+                        .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
+                        .transform(&display::PhysicalDisplay::isInternal)
+                        .value_or(false)};
     }
 
     /*
@@ -926,11 +958,12 @@
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
-    void processDisplayChangesLocked() REQUIRES(mStateLock);
+    void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
     void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
-                               const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
+                               const DisplayDeviceState& drawingState)
+            REQUIRES(mStateLock, kMainThreadContext);
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
@@ -959,21 +992,16 @@
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+    sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
             REQUIRES(mStateLock) {
-        const sp<IBinder> nullToken;
-        return mPhysicalDisplayTokens.get(displayId).value_or(std::cref(nullToken));
+        const sp<display::DisplayToken> nullToken;
+        return mPhysicalDisplays.get(displayId)
+                .transform([](const display::PhysicalDisplay& display) { return display.token(); })
+                .value_or(std::cref(nullToken));
     }
 
     std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
-            const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
-        for (const auto& [id, token] : mPhysicalDisplayTokens) {
-            if (token == displayToken) {
-                return id;
-            }
-        }
-        return {};
-    }
+            const sp<display::DisplayToken>&) const REQUIRES(mStateLock);
 
     // Returns the first display connected at boot.
     //
@@ -996,7 +1024,8 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
-    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
+    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+            REQUIRES(mStateLock, kMainThreadContext);
 
     void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
 
@@ -1063,14 +1092,14 @@
     /*
      * Misc
      */
-    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
+    std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId) REQUIRES(mStateLock);
 
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
     void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
-            REQUIRES(mStateLock);
+            REQUIRES(mStateLock, kMainThreadContext);
 
     bool isHdrLayer(Layer* layer) const;
 
@@ -1164,12 +1193,10 @@
     // Displays are composited in `mDisplays` order. Internal displays are inserted at boot and
     // never removed, so take precedence over external and virtual displays.
     //
-    // The static capacities were chosen to exceed a typical number of physical/virtual displays.
-    //
     // May be read from any thread, but must only be written from the main thread.
-    ftl::SmallMap<wp<IBinder>, const sp<DisplayDevice>, 5> mDisplays GUARDED_BY(mStateLock);
-    ftl::SmallMap<PhysicalDisplayId, const sp<IBinder>, 3> mPhysicalDisplayTokens
-            GUARDED_BY(mStateLock);
+    display::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+
+    display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
 
     struct {
         DisplayIdGenerator<GpuVirtualDisplayId> gpu;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 15a791e..319d014 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -24,7 +24,6 @@
 
 #include "BufferStateLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
@@ -94,8 +93,8 @@
     return sp<BufferStateLayer>::make(args);
 }
 
-sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
-    return sp<EffectLayer>::make(args);
+sp<Layer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
 std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 8d00379..6602240 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -42,7 +42,7 @@
             const sp<IGraphicBufferProducer>&) override;
     std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
     sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 291838f..dc2afd3 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -33,7 +33,6 @@
 class BufferLayerConsumer;
 class BufferStateLayer;
 class DisplayDevice;
-class EffectLayer;
 class FrameTracer;
 class GraphicBuffer;
 class HWComposer;
@@ -91,7 +90,7 @@
     virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
 
     virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 312d4ab..6501e20 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -84,9 +84,7 @@
         return sp<BufferStateLayer>::make(args);
     }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) {
-        return sp<EffectLayer>::make(args);
-    }
+    sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
         return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md
index 7a5f229..a06c41b 100644
--- a/services/surfaceflinger/fuzzer/README.md
+++ b/services/surfaceflinger/fuzzer/README.md
@@ -78,9 +78,8 @@
 Layer supports the following parameters:
 1. Display Connection Types (parameter name: `fakeDisplay`)
 2. State Sets (parameter name: `traverseInZOrder`)
-3. State Subsets (parameter name: `prepareCompositionState`)
-4. Disconnect modes (parameter name: `disconnect`)
-5. Data Spaces (parameter name: `setDataspace`)
+3. Disconnect modes (parameter name: `disconnect`)
+4. Data Spaces (parameter name: `setDataspace`)
 
 You can find the possible values in the fuzzer's source code.
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 28b875a..79112bd 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -237,7 +237,8 @@
 
     mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool());
 
-    mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>());
+    FTL_FAKE_GUARD(kMainThreadContext,
+                   mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>()));
 
     mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>());
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 3aa3633..2297618 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -33,7 +33,6 @@
 #include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
-#include "EffectLayer.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -360,8 +359,8 @@
         return nullptr;
     }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override {
-        return sp<EffectLayer>::make(args);
+    sp<Layer> createEffectLayer(const LayerCreationArgs &args) override {
+        return sp<Layer>::make(args);
     }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
@@ -600,14 +599,18 @@
         mFlinger->binderDied(display);
         mFlinger->onFirstRef();
 
-        mFlinger->commitTransactions();
         mFlinger->updateInputFlinger();
         mFlinger->updateCursorAsync();
 
         setVsyncConfig(&mFdp);
 
-        FTL_FAKE_GUARD(kMainThreadContext,
-                       mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)));
+        {
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            mFlinger->commitTransactions();
+            mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
+            mFlinger->postComposition();
+        }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
         mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -624,8 +627,6 @@
 
         mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
 
-        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postComposition());
-
         mFlinger->calculateExpectedPresentTime({});
 
         mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
@@ -664,7 +665,8 @@
         }
 
         mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-        const auto fps = mRefreshRateConfigs->getActiveMode()->getFps();
+        const auto fps =
+                FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateConfigs->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -715,9 +717,9 @@
 
     void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
 
-    auto commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags) FTL_FAKE_GUARD(kMainThreadContext) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        return mFlinger->commitTransactionsLocked(transactionFlags);
+        mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     auto setDisplayStateLocked(const DisplayState &s) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index aeccc52..9ece260 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -17,7 +17,6 @@
 #include <BufferStateLayer.h>
 #include <Client.h>
 #include <DisplayDevice.h>
-#include <EffectLayer.h>
 #include <LayerRenderArea.h>
 #include <ftl/future.h>
 #include <fuzzer/FuzzedDataProvider.h>
@@ -79,13 +78,13 @@
     TestableSurfaceFlinger flinger;
     sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
     const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client);
-    sp<EffectLayer> effectLayer = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> effectLayer = sp<Layer>::make(layerCreationArgs);
 
     effectLayer->setColor({(mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*x*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*y*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*z*/)});
     effectLayer->setDataspace(mFdp.PickValueInArray(kDataspaces));
-    sp<EffectLayer> parent = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> parent = sp<Layer>::make(layerCreationArgs);
     effectLayer->setChildrenDrawingParent(parent);
 
     const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo();
@@ -149,7 +148,8 @@
     layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
 
     layer->fenceHasSignaled();
-    layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
+    layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>(),
+                            false /*updatingOutputGeometryThisFrame*/);
     const std::vector<sp<CallbackHandle>> callbacks;
     layer->setTransactionCompletedListeners(callbacks);
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 9584492..3fc2b7e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -366,7 +366,7 @@
             {modeId,
              {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
               Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-    refreshRateConfigs.setActiveModeId(modeId);
+    FTL_FAKE_GUARD(kMainThreadContext, refreshRateConfigs.setActiveModeId(modeId));
 
     RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
                                                            mFdp.ConsumeFloatingPoint<float>()),
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 513fdc3..c206e1f 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -154,6 +154,36 @@
 
     ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
 }
+
+// b/245052266 - we possible could support blur and a buffer at the same layer but
+// might break existing assumptions at higher level. This test captures the current
+// expectations. A layer drawing a buffer will not support blur.
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverBlur) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setBackgroundBlurRadius(layer, 5).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverBlur");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setColor(layer, {Color::GREEN.r, Color::GREEN.g, Color::GREEN.b}).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverColor");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 77625b3..9485f48 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -38,7 +38,6 @@
 #include <utils/String8.h>
 
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -309,16 +308,20 @@
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs);
 
+        constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal;
+        constexpr bool kIsPrimary = true;
+
         test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                                   ui::DisplayConnectionType::Internal, HWC_DISPLAY,
-                                                   true /* isPrimary */)
+                                                   kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
                                  .setDisplaySurface(test->mDisplaySurface)
                                  .setNativeWindow(test->mNativeWindow)
                                  .setSecure(Derived::IS_SECURE)
                                  .setPowerMode(Derived::INIT_POWER_MODE)
                                  .inject();
         Mock::VerifyAndClear(test->mNativeWindow.get());
-        test->mDisplay->setLayerStack(LAYER_STACK);
+
+        constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal;
+        test->mDisplay->setLayerFilter({LAYER_STACK, kIsInternal});
     }
 
     template <typename Case>
@@ -853,13 +856,13 @@
 template <typename LayerProperties>
 struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<EffectLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
-            return sp<EffectLayer>::make(
-                    LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
-                                      LayerProperties::LAYER_FLAGS, LayerMetadata()));
+        FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+            return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(),
+                                                     "test-layer", LayerProperties::LAYER_FLAGS,
+                                                     LayerMetadata()));
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
@@ -941,12 +944,12 @@
 template <typename LayerProperties>
 struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<EffectLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
                                LayerProperties::LAYER_FLAGS, LayerMetadata());
-        FlingerLayerType layer = sp<EffectLayer>::make(args);
+        FlingerLayerType layer = sp<Layer>::make(args);
         Base::template initLayerDrawingStateAndComputeBounds(test, layer);
         return layer;
     }
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index ec27eda..67ace1a 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -292,9 +292,10 @@
 
 TEST_F(DispSyncSourceTest, getLatestVsyncData) {
     const nsecs_t now = systemTime();
-    const nsecs_t vsyncInternalDuration = mWorkDuration.count() + mReadyDuration.count();
+    const nsecs_t expectedPresentationTime =
+            now + mWorkDuration.count() + mReadyDuration.count() + 1;
     EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
-            .WillOnce(Return(now + vsyncInternalDuration + 1));
+            .WillOnce(Return(expectedPresentationTime));
     {
         InSequence seq;
         EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
@@ -306,10 +307,8 @@
     EXPECT_TRUE(mDispSyncSource);
 
     const auto vsyncData = mDispSyncSource->getLatestVSyncData();
-    ASSERT_GT(vsyncData.deadlineTimestamp, now);
-    ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
-    EXPECT_EQ(vsyncData.deadlineTimestamp,
-              vsyncData.expectedPresentationTime - vsyncInternalDuration);
+    ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
+    EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 0b4e196..9789df5 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -25,7 +25,6 @@
 #include <gui/LayerMetadata.h>
 
 #include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
index 4974f90..95e54f6 100644
--- a/services/surfaceflinger/tests/unittests/LayerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp
@@ -17,7 +17,6 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
-#include <EffectLayer.h>
 #include <gtest/gtest.h>
 #include <ui/FloatRect.h>
 #include <ui/Transform.h>
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
index b7a8a93..14304d1 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -35,7 +35,7 @@
 sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return sp<EffectLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 std::string PrintToStringParamName(
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
index fc9b6a2..ab446fa 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -23,8 +23,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 188fd58..5d9b2a8 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "SchedulerUnittests"
 
 #include <ftl/enum.h>
+#include <ftl/fake_guard.h>
 #include <gmock/gmock.h>
 #include <log/log.h>
 #include <ui/Size.h>
@@ -41,6 +42,16 @@
 struct TestableRefreshRateConfigs : RefreshRateConfigs {
     using RefreshRateConfigs::RefreshRateConfigs;
 
+    void setActiveModeId(DisplayModeId modeId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::setActiveModeId(modeId);
+    }
+
+    const DisplayMode& getActiveMode() const {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::getActiveMode();
+    }
+
     DisplayModePtr getMinSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
         return mMinRefreshRateModeIt->second;
@@ -243,20 +254,20 @@
 TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId60);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId60);
     }
 
     configs.setActiveModeId(kModeId90);
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
     }
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
     }
 }
 
@@ -966,6 +977,32 @@
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
+TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
+    RefreshRateConfigs configs(kModes_60_90, kModeId60);
+
+    auto [refreshRate, signals] = configs.getBestRefreshRate({}, {});
+    EXPECT_FALSE(signals.powerOnImminent);
+    EXPECT_EQ(kMode90, refreshRate);
+
+    std::tie(refreshRate, signals) = configs.getBestRefreshRate({}, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+    EXPECT_EQ(kMode90, refreshRate);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr1 = layers[0];
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+
+    std::tie(refreshRate, signals) = configs.getBestRefreshRate(layers, {.powerOnImminent = false});
+    EXPECT_FALSE(signals.powerOnImminent);
+    EXPECT_EQ(kMode60, refreshRate);
+
+    std::tie(refreshRate, signals) = configs.getBestRefreshRate(layers, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+    EXPECT_EQ(kMode90, refreshRate);
+}
+
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
     RefreshRateConfigs configs(kModes_60_90, kModeId60);
 
@@ -1898,30 +1935,30 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = configs.getActiveMode()->getFps();
+    Fps displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId60);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId72);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId120);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 6752a39..abf1786 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -22,7 +22,6 @@
 #include <gui/LayerMetadata.h>
 
 #include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -60,7 +59,7 @@
 
     void setupScheduler();
     sp<BufferStateLayer> createBufferStateLayer();
-    sp<EffectLayer> createEffectLayer();
+    sp<Layer> createEffectLayer();
 
     void setParent(Layer* child, Layer* parent);
     void commitTransaction(Layer* layer);
@@ -96,10 +95,10 @@
     return sp<BufferStateLayer>::make(args);
 }
 
-sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
+sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return sp<EffectLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6ee8174..51c6bea 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -25,7 +25,6 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
@@ -374,11 +373,6 @@
     const auto& layerFactory = GetParam();
 
     auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    if (!parent->isVisible()) {
-        // This is a hack as all the test layers except EffectLayer are not visible,
-        // but since the logic is unified in Layer, it should be fine.
-        return;
-    }
 
     auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
     addChild(parent, child);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 71f1a2b..73f654b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -118,9 +118,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {.id = *displayId,
-                            .type = *connectionType,
-                            .hwcDisplayId = *hwcDisplayId};
+        expectedPhysical = {.id = *displayId, .hwcDisplayId = *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
@@ -145,10 +143,13 @@
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-    const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-    ASSERT_TRUE(displayTokenOpt);
+    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+    ASSERT_TRUE(displayOpt);
 
-    verifyDisplayIsConnected<Case>(displayTokenOpt->get());
+    const auto& display = displayOpt->get();
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, display.snapshot().connectionType());
+
+    verifyDisplayIsConnected<Case>(display.token());
 }
 
 void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
@@ -247,10 +248,10 @@
     // HWComposer should not have an entry for the display
     EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
+    // SF should not have a PhysicalDisplay.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
 
     // The existing token should have been removed.
     verifyDisplayIsNotConnected(existing.token());
@@ -329,10 +330,10 @@
                 // HWComposer should not have an entry for the display
                 EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-                // SF should not have a display token.
+                // SF should not have a PhysicalDisplay.
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-                ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
             }(),
             testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
@@ -376,9 +377,9 @@
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-                const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-                ASSERT_TRUE(displayTokenOpt);
-                EXPECT_NE(existing.token(), displayTokenOpt->get());
+                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+                ASSERT_TRUE(displayOpt);
+                EXPECT_NE(existing.token(), displayOpt->get().token());
 
                 // A new display should be connected in its place.
                 verifyPhysicalDisplayIsConnected<Case>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 1756368..9e54083 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -253,14 +253,16 @@
     using DispSync = DispSyncVariant;
     using Transition = TransitionVariant;
 
-    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+    static sp<DisplayDevice> injectDisplayWithInitialPowerMode(DisplayTransactionTest* test,
+                                                               PowerMode mode) {
         Display::injectHwcDisplayWithNoDefaultCapabilities(test);
-        auto display = Display::makeFakeExistingDisplayInjector(test);
-        display.inject();
-        display.mutableDisplayDevice()->setPowerMode(mode);
-        if (display.mutableDisplayDevice()->isInternal()) {
-            test->mFlinger.mutableActiveDisplayToken() =
-                    display.mutableDisplayDevice()->getDisplayToken();
+        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.mutableActiveDisplayToken() = display->getDisplayToken();
         }
 
         return display;
@@ -353,8 +355,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
-                                  Case::Transition::TARGET_POWER_MODE);
+    mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
 
     // --------------------------------------------------------------------
     // Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 6aeb3fe..ec2c2b4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -246,11 +246,14 @@
                                             .setDpiY(DEFAULT_DPI)
                                             .setGroup(0)
                                             .build();
+
         state.physical = {.id = *displayId,
-                          .type = *connectionType,
                           .hwcDisplayId = *hwcDisplayId,
-                          .supportedModes = makeModes(activeMode),
-                          .activeMode = std::move(activeMode)};
+                          .activeMode = activeMode};
+
+        mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
+                                                              *connectionType,
+                                                              makeModes(activeMode), std::nullopt);
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -264,7 +267,6 @@
 
     ASSERT_TRUE(device != nullptr);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
-    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
@@ -280,7 +282,6 @@
               device->receivesInput());
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
-        EXPECT_EQ(1, device->getSupportedModes().size());
         EXPECT_NE(nullptr, device->getActiveMode());
         EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId());
     }
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6c6c9aa..1ce6e18 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -32,7 +32,6 @@
 
 #include "BufferStateLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -125,7 +124,7 @@
         return nullptr;
     }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
+    sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
         return std::make_unique<mock::FrameTracer>();
@@ -223,7 +222,7 @@
             configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
         }
 
-        const auto fps = configs->getActiveMode()->getFps();
+        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, configs->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -371,6 +370,7 @@
 
     void commitTransactionsLocked(uint32_t transactionFlags) {
         Mutex::Autolock lock(mFlinger->mStateLock);
+        ftl::FakeGuard guard(kMainThreadContext);
         mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
@@ -464,6 +464,7 @@
 
     void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
+        ftl::FakeGuard guard(kMainThreadContext);
         mFlinger->onActiveDisplayChangedLocked(activeDisplay);
     }
 
@@ -503,6 +504,7 @@
      */
 
     const auto& displays() const { return mFlinger->mDisplays; }
+    const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; }
     const auto& currentState() const { return mFlinger->mCurrentState; }
     const auto& drawingState() const { return mFlinger->mDrawingState; }
     const auto& transactionFlags() const { return mFlinger->mTransactionFlags; }
@@ -515,12 +517,12 @@
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
+    auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
-    auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
     auto& mutableTexturePool() { return mFlinger->mTexturePool; }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
@@ -725,14 +727,20 @@
               : mFlinger(flinger),
                 mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken,
                               display),
+                mConnectionType(connectionType),
                 mHwcDisplayId(hwcDisplayId) {
-            mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
             mCreationArgs.initialPowerMode = hal::PowerMode::ON;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
 
+        auto physicalDisplay() const {
+            return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
+                    .and_then(&PhysicalDisplayId::tryCast)
+                    .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
+        }
+
         DisplayDeviceState& mutableDrawingDisplayState() {
             return mFlinger.mutableDrawingState().displays.editValueFor(mDisplayToken);
         }
@@ -760,7 +768,7 @@
         // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
         auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
                               std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
-            mCreationArgs.supportedModes = std::move(modes);
+            mDisplayModes = std::move(modes);
             mCreationArgs.activeModeId = activeModeId;
             mCreationArgs.refreshRateConfigs = std::move(configs);
             return *this;
@@ -806,7 +814,7 @@
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
-            auto& modes = mCreationArgs.supportedModes;
+            auto& modes = mDisplayModes;
             auto& activeModeId = mCreationArgs.activeModeId;
 
             if (displayId && !mCreationArgs.refreshRateConfigs) {
@@ -834,8 +842,13 @@
                 }
             }
 
+            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
+            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
+
             DisplayDeviceState state;
-            if (const auto type = mCreationArgs.connectionType) {
+            state.isSecure = mCreationArgs.isSecure;
+
+            if (mConnectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
@@ -845,29 +858,21 @@
                 LOG_ALWAYS_FATAL_IF(!activeMode);
 
                 state.physical = {.id = *physicalId,
-                                  .type = *type,
                                   .hwcDisplayId = *mHwcDisplayId,
-                                  .deviceProductInfo = {},
-                                  .supportedModes = modes,
                                   .activeMode = activeMode->get()};
-            }
 
-            state.isSecure = mCreationArgs.isSecure;
+                const auto it = mFlinger.mutablePhysicalDisplays()
+                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+                                                            *mConnectionType, std::move(modes),
+                                                            std::nullopt)
+                                        .first;
 
-            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
-            if (!display->isVirtual()) {
-                display->setActiveMode(activeModeId);
+                display->setActiveMode(activeModeId, it->second.snapshot());
             }
-            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (const auto& physical = state.physical) {
-                mFlinger.mutablePhysicalDisplayTokens().emplace_or_replace(physical->id,
-                                                                           mDisplayToken);
-            }
-
             return display;
         }
 
@@ -875,6 +880,8 @@
         TestableSurfaceFlinger& mFlinger;
         sp<BBinder> mDisplayToken = sp<BBinder>::make();
         DisplayDeviceCreationArgs mCreationArgs;
+        DisplayModes mDisplayModes;
+        const std::optional<ui::DisplayConnectionType> mConnectionType;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };