Merge "SF: Tie main thread priority to active display" into udc-dev
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 5cbcf9f..a417837 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1001,7 +1001,7 @@
     // These traces are always enabled, i.e. they are always pre-dumped
     const std::vector<std::filesystem::path> uiTraces = {
         std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"},
-        std::filesystem::path{"/data/misc/wmtrace/transition_trace.winscope"},
+        std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"},
         std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"},
     };
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 33bb343..80fed98 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -226,6 +226,7 @@
         "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
         "LayerMetadata.cpp",
+        "LayerStatePermissions.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index cefb9a7..d72f65e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -62,7 +62,7 @@
     status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                  Vector<ComposerState>& state, const Vector<DisplayState>& displays,
                                  uint32_t flags, const sp<IBinder>& applyToken,
-                                 const InputWindowCommands& commands, int64_t desiredPresentTime,
+                                 InputWindowCommands commands, int64_t desiredPresentTime,
                                  bool isAutoTimestamp,
                                  const std::vector<client_cache_t>& uncacheBuffers,
                                  bool hasListenerCallbacks,
@@ -188,9 +188,9 @@
             SAFE_PARCEL(data.readUint64, &transactionId);
 
             return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
-                                       inputWindowCommands, desiredPresentTime, isAutoTimestamp,
-                                       uncacheBuffers, hasListenerCallbacks, listenerCallbacks,
-                                       transactionId);
+                                       std::move(inputWindowCommands), desiredPresentTime,
+                                       isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
+                                       listenerCallbacks, transactionId);
         }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
diff --git a/libs/gui/LayerStatePermissions.cpp b/libs/gui/LayerStatePermissions.cpp
new file mode 100644
index 0000000..28697ca
--- /dev/null
+++ b/libs/gui/LayerStatePermissions.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IPCThreadState.h>
+#include <gui/LayerStatePermissions.h>
+#include <private/android_filesystem_config.h>
+#ifndef __ANDROID_VNDK__
+#include <binder/PermissionCache.h>
+#endif // __ANDROID_VNDK__
+#include <gui/LayerState.h>
+
+namespace android {
+std::unordered_map<std::string, int> LayerStatePermissions::mPermissionMap = {
+        // If caller has ACCESS_SURFACE_FLINGER, they automatically get ROTATE_SURFACE_FLINGER
+        // permission, as well
+        {"android.permission.ACCESS_SURFACE_FLINGER",
+         layer_state_t::Permission::ACCESS_SURFACE_FLINGER |
+                 layer_state_t::Permission::ROTATE_SURFACE_FLINGER},
+        {"android.permission.ROTATE_SURFACE_FLINGER",
+         layer_state_t::Permission::ROTATE_SURFACE_FLINGER},
+        {"android.permission.INTERNAL_SYSTEM_WINDOW",
+         layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW},
+};
+
+static bool callingThreadHasPermission(const std::string& permission __attribute__((unused)),
+                                       int pid __attribute__((unused)),
+                                       int uid __attribute__((unused))) {
+#ifndef __ANDROID_VNDK__
+    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+            PermissionCache::checkPermission(String16(permission.c_str()), pid, uid);
+#endif // __ANDROID_VNDK__
+    return false;
+}
+
+uint32_t LayerStatePermissions::getTransactionPermissions(int pid, int uid) {
+    uint32_t permissions = 0;
+    for (auto [permissionName, permissionVal] : mPermissionMap) {
+        if (callingThreadHasPermission(permissionName, pid, uid)) {
+            permissions |= permissionVal;
+        }
+    }
+
+    return permissions;
+}
+} // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index eb5cc4f..1b13ec1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -54,6 +54,7 @@
 #include <ui/DynamicDisplayInfo.h>
 
 #include <android-base/thread_annotations.h>
+#include <gui/LayerStatePermissions.h>
 #include <private/gui/ComposerService.h>
 #include <private/gui/ComposerServiceAIDL.h>
 
@@ -716,11 +717,16 @@
     mListenerCallbacks = other.mListenerCallbacks;
 }
 
-void SurfaceComposerClient::Transaction::sanitize() {
+void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
+    uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid);
     for (auto & [handle, composerState] : mComposerStates) {
-        composerState.state.sanitize(0 /* permissionMask */);
+        composerState.state.sanitize(permissions);
     }
-    mInputWindowCommands.clear();
+    if (!mInputWindowCommands.empty() &&
+        (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
+        ALOGE("Only privileged callers are allowed to send input commands.");
+        mInputWindowCommands.clear();
+    }
 }
 
 std::unique_ptr<SurfaceComposerClient::Transaction>
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 1e67225..bd21851 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -113,7 +113,7 @@
     virtual status_t setTransactionState(
             const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+            InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
             bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
             bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
             uint64_t transactionId) = 0;
diff --git a/libs/gui/include/gui/LayerStatePermissions.h b/libs/gui/include/gui/LayerStatePermissions.h
new file mode 100644
index 0000000..a90f30c
--- /dev/null
+++ b/libs/gui/include/gui/LayerStatePermissions.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+class LayerStatePermissions {
+public:
+    static uint32_t getTransactionPermissions(int pid, int uid);
+
+private:
+    static std::unordered_map<std::string, int> mPermissionMap;
+};
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 945b164..8d2cdaf 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -744,7 +744,7 @@
          *
          * TODO (b/213644870): Remove all permissioned things from Transaction
          */
-        void sanitize();
+        void sanitize(int pid, int uid);
 
         static sp<IBinder> getDefaultApplyToken();
         static void setDefaultApplyToken(sp<IBinder> applyToken);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index fccc408..5bc6904 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -699,7 +699,7 @@
                                  Vector<ComposerState>& /*state*/,
                                  const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                                  const sp<IBinder>& /*applyToken*/,
-                                 const InputWindowCommands& /*inputWindowCommands*/,
+                                 InputWindowCommands /*inputWindowCommands*/,
                                  int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
                                  const std::vector<client_cache_t>& /*cachedBuffer*/,
                                  bool /*hasListenerCallbacks*/,
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 0f7aa27..ef927e3 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -534,7 +534,7 @@
       if (mQueuedAllJobs) {
         return false;
       } else {
-        mCv.wait(lock);
+        mCv.wait_for(lock, std::chrono::milliseconds(100));
       }
     } else {
       auto it = mJobs.begin();
@@ -557,6 +557,8 @@
 void JobQueue::markQueueForEnd() {
   std::unique_lock<std::mutex> lock{mMutex};
   mQueuedAllJobs = true;
+  lock.unlock();
+  mCv.notify_all();
 }
 
 void JobQueue::reset() {
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 9823fc8..525fed1 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -326,10 +326,10 @@
         // device's present timestamps are reliable (which may not be the case on emulators).
         if (cnx->useAngle) {
             if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
-                mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+                mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
             }
         } else {
-            mExtensionString.append("EGL_ANDROID_get_frame_timestamps");
+            mExtensionString.append("EGL_ANDROID_get_frame_timestamps ");
         }
 
         hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 326ca87..f3ada8e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2476,7 +2476,8 @@
                 newTouchedWindowHandle = nullptr;
             }
 
-            if (!haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
+            if (newTouchedWindowHandle != nullptr &&
+                !haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
                 ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
                       oldTouchedWindowHandle->getName().c_str(),
                       newTouchedWindowHandle->getName().c_str(), displayId);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index b86906b..721bb0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -203,7 +203,7 @@
 
     using Change = InputReaderConfiguration::Change;
 
-    if (!isIgnored()) {
+    if (!changes.any() || !isIgnored()) {
         // Full configuration should happen the first time configure is called
         // and when the device type is changed. Changing a device type can
         // affect various other parameters so should result in a
@@ -503,9 +503,9 @@
         classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
         mappers.push_back(std::make_unique<TouchpadInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
-        mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH)) {
-        mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
     }
 
     // Joystick-like devices.
@@ -520,7 +520,7 @@
 
     // External stylus-like devices.
     if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
-        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<ExternalStylusInputMapper>(contextPtr, readerConfig));
     }
     return mappers;
 }
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 6303546..0b8a608 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -149,6 +149,16 @@
         return *mapper;
     }
 
+    template <class T, typename... Args>
+    T& constructAndAddMapper(int32_t eventHubId, Args... args) {
+        // create mapper
+        auto& devicePair = mDevices[eventHubId];
+        auto& deviceContext = devicePair.first;
+        auto& mappers = devicePair.second;
+        mappers.push_back(createInputMapper<T>(*deviceContext, args...));
+        return static_cast<T&>(*mappers.back());
+    }
+
     // construct and add a controller to the input device
     template <class T>
     T& addController(int32_t eventHubId) {
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 841c437..97df02b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -26,8 +26,10 @@
 
 class ExternalStylusInputMapper : public InputMapper {
 public:
-    explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext,
-                                       const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~ExternalStylusInputMapper() = default;
 
     uint32_t getSources() const override;
@@ -46,6 +48,8 @@
 
     StylusState mStylusState;
 
+    explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext,
+                                       const InputReaderConfiguration& readerConfig);
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index f017317..06de4c2 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -25,6 +25,20 @@
 #include "VibrationElement.h"
 
 namespace android {
+/**
+ * This is the factory method that must be used to create any InputMapper
+ */
+template <class T, class... Args>
+std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                     const InputReaderConfiguration& readerConfig, Args... args) {
+    // Using `new` to access non-public constructors.
+    std::unique_ptr<T> mapper(new T(deviceContext, readerConfig, args...));
+    // We need to reset and configure the mapper to ensure it is ready to process event
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    std::list<NotifyArgs> unused = mapper->reset(now);
+    unused += mapper->reconfigure(now, readerConfig, /*changes=*/{});
+    return mapper;
+}
 
 /* An input mapper transforms raw input events into cooked event data.
  * A single input device can have multiple associated input mappers in order to interpret
@@ -39,8 +53,15 @@
  */
 class InputMapper {
 public:
-    explicit InputMapper(InputDeviceContext& deviceContext,
-                         const InputReaderConfiguration& readerConfig);
+    /**
+     * Subclasses must either provide a public constructor
+     * or must be-friend the factory method.
+     */
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
+
     virtual ~InputMapper();
 
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
@@ -102,6 +123,9 @@
 protected:
     InputDeviceContext& mDeviceContext;
 
+    explicit InputMapper(InputDeviceContext& deviceContext,
+                         const InputReaderConfiguration& readerConfig);
+
     status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
     void bumpGeneration();
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index a617420..1d788df 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -23,8 +23,11 @@
 
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
-    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
-                                   const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
+
     ~MultiTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
@@ -36,6 +39,8 @@
     bool hasStylus() const override;
 
 private:
+    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+                                   const InputReaderConfiguration& readerConfig);
     // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
     // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
     // It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 9341007..7726bfb 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -23,8 +23,11 @@
 
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
-    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
-                                    const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
+
     ~SingleTouchInputMapper() override;
 
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
@@ -37,6 +40,8 @@
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+                                    const InputReaderConfiguration& readerConfig);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index c72a263..f4d50b8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -125,8 +125,7 @@
                                    const InputReaderConfiguration& readerConfig)
       : InputMapper(deviceContext, readerConfig),
         mTouchButtonAccumulator(deviceContext),
-        mConfig(readerConfig),
-        mParameters(computeParameters(deviceContext)) {}
+        mConfig(readerConfig) {}
 
 TouchInputMapper::~TouchInputMapper() {}
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7141924..d8b59ca 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -146,8 +146,6 @@
 
 class TouchInputMapper : public InputMapper {
 public:
-    explicit TouchInputMapper(InputDeviceContext& deviceContext,
-                              const InputReaderConfiguration& readerConfig);
     ~TouchInputMapper() override;
 
     uint32_t getSources() const override;
@@ -358,25 +356,28 @@
     nsecs_t mExternalStylusFusionTimeout;
     bool mExternalStylusDataPending;
     // A subset of the buttons in mCurrentRawState that came from an external stylus.
-    int32_t mExternalStylusButtonsApplied;
+    int32_t mExternalStylusButtonsApplied{0};
 
     // True if we sent a HOVER_ENTER event.
-    bool mSentHoverEnter;
+    bool mSentHoverEnter{false};
 
     // Have we assigned pointer IDs for this stream
-    bool mHavePointerIds;
+    bool mHavePointerIds{false};
 
     // Is the current stream of direct touch events aborted
-    bool mCurrentMotionAborted;
+    bool mCurrentMotionAborted{false};
 
     // The time the primary pointer last went down.
-    nsecs_t mDownTime;
+    nsecs_t mDownTime{0};
 
     // The pointer controller, or null if the device is not a pointer.
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
     std::vector<VirtualKey> mVirtualKeys;
 
+    explicit TouchInputMapper(InputDeviceContext& deviceContext,
+                              const InputReaderConfiguration& readerConfig);
+
     virtual void dumpParameters(std::string& dump);
     virtual void configureRawPointerAxes();
     virtual void dumpRawPointerAxes(std::string& dump);
@@ -513,7 +514,7 @@
         STYLUS,
         MOUSE,
     };
-    PointerUsage mPointerUsage;
+    PointerUsage mPointerUsage{PointerUsage::NONE};
 
     struct PointerGesture {
         enum class Mode {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index a6cdee5..f6f02d8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5535,6 +5535,46 @@
                                            AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
 }
 
+/**
+ * Two windows, one on the left and another on the right. The left window is slippery. The right
+ * window isn't eligible to receive touch because it specifies InputConfig::DROP_INPUT. When the
+ * touch moves from the left window into the right window, the gesture should continue to go to the
+ * left window. Touch shouldn't slip because the right window can't receive touches. This test
+ * reproduces a crash.
+ */
+TEST_F(InputDispatcherTest, TouchSlippingIntoWindowThatDropsTouches) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    sp<FakeWindowHandle> leftSlipperyWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    leftSlipperyWindow->setSlippery(true);
+    leftSlipperyWindow->setFrame(Rect(0, 0, 100, 100));
+
+    sp<FakeWindowHandle> rightDropTouchesWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    rightDropTouchesWindow->setFrame(Rect(100, 0, 200, 100));
+    rightDropTouchesWindow->setDropInput(true);
+
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {leftSlipperyWindow, rightDropTouchesWindow}}});
+
+    // Start touch in the left window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .build());
+    leftSlipperyWindow->consumeMotionDown();
+
+    // And move it into the right window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+                                      .build());
+
+    // Since the right window isn't eligible to receive input, touch does not slip.
+    // The left window continues to receive the gesture.
+    leftSlipperyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+    rightDropTouchesWindow->assertNoEvents();
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index d969034..2b6655c 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -73,6 +73,17 @@
         return mapper;
     }
 
+    template <class T, typename... Args>
+    T& constructAndAddMapper(Args... args) {
+        // ensure a device entry exists for this eventHubId
+        mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
+        // configure the empty device
+        configureDevice(/*changes=*/{});
+
+        return mDevice->constructAndAddMapper<T>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+                                                 args...);
+    }
+
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
                                       ui::Rotation orientation, const std::string& uniqueId,
                                       std::optional<uint8_t> physicalPort,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d56db11..ae3ae6c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -5069,7 +5069,7 @@
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
@@ -5078,7 +5078,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 }
@@ -5089,7 +5089,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Unknown key.
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
@@ -5117,7 +5117,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Unknown key.
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
@@ -5145,7 +5145,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     uint8_t flags[2] = { 0, 0 };
     ASSERT_TRUE(
@@ -5160,7 +5160,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5210,7 +5210,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5331,7 +5331,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5406,7 +5406,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5502,7 +5502,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5592,7 +5592,7 @@
     prepareAxes(POSITION);
     // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
     // need to be rotated. Touchscreens are orientation-aware by default.
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -5617,7 +5617,7 @@
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     addConfigurationProperty("touch.orientationAware", "0");
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -5686,7 +5686,7 @@
     addConfigurationProperty("touch.orientation", "ORIENTATION_0");
     clearViewports();
     prepareDisplay(ui::ROTATION_0);
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
     // Orientation 0.
@@ -5710,7 +5710,7 @@
     addConfigurationProperty("touch.orientation", "ORIENTATION_90");
     clearViewports();
     prepareDisplay(ui::ROTATION_0);
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
     // Orientation 90.
@@ -5734,7 +5734,7 @@
     addConfigurationProperty("touch.orientation", "ORIENTATION_180");
     clearViewports();
     prepareDisplay(ui::ROTATION_0);
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
     // Orientation 180.
@@ -5758,7 +5758,7 @@
     addConfigurationProperty("touch.orientation", "ORIENTATION_270");
     clearViewports();
     prepareDisplay(ui::ROTATION_0);
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     NotifyMotionArgs args;
 
     // Orientation 270.
@@ -5782,7 +5782,7 @@
     // orientation-aware are affected by display rotation.
     addConfigurationProperty("touch.orientationAware", "0");
     addConfigurationProperty("touch.orientation", "ORIENTATION_90");
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -5849,7 +5849,7 @@
     prepareAxes(POSITION);
     addConfigurationProperty("touch.orientationAware", "1");
     prepareDisplay(ui::ROTATION_0);
-    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Set a physical frame in the display viewport.
     auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
@@ -5903,7 +5903,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5948,7 +5948,7 @@
     prepareLocationCalibration();
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     int32_t rawX = 100;
     int32_t rawY = 200;
@@ -5970,7 +5970,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -6213,7 +6213,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6349,7 +6349,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6420,7 +6420,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6491,7 +6491,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Touch down.
     processDown(mapper, 100, 200);
@@ -6513,7 +6513,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Set the initial state for the touch pointer.
     mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
@@ -6541,7 +6541,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     NotifyMotionArgs motionArgs;
 
     // Down.
@@ -6569,7 +6569,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
     NotifyMotionArgs motionArgs;
 
@@ -6629,7 +6629,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     // Press a stylus button.
@@ -6670,7 +6670,7 @@
 
     mFakePolicy->setStylusButtonMotionEventsEnabled(false);
 
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     // Press a stylus button.
@@ -6707,7 +6707,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
@@ -6723,7 +6723,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setStylusPointerIconEnabled(true);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     processKey(mapper, BTN_TOOL_PEN, 1);
     processMove(mapper, 100, 200);
@@ -6747,7 +6747,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setStylusPointerIconEnabled(false);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     processKey(mapper, BTN_TOOL_PEN, 1);
     processMove(mapper, 100, 200);
@@ -6765,7 +6765,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Ensure that the device is created as a touchscreen, not touch navigation.
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
@@ -6799,7 +6799,7 @@
     mFakePolicy->updateViewport(*viewport);
     configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Hovering inside the physical frame produces events.
     processKey(mapper, BTN_TOOL_PEN, 1);
@@ -6905,7 +6905,7 @@
 
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6940,7 +6940,7 @@
 
     prepareButtons();
     prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -7069,7 +7069,7 @@
     addConfigurationProperty("touch.orientation", ftl::enum_string(touchscreenOrientation).c_str());
     prepareDisplay(ui::ROTATION_0);
 
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // If the touchscreen is installed in a rotated orientation relative to the display (i.e. in
     // orientations of either 90 or 270) this means the display's natural resolution will be
@@ -7121,7 +7121,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(displayRotation);
 
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     const auto& expectedPoints = kMappedCorners.at(displayRotation);
 
@@ -7159,7 +7159,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.orientation", "ORIENTATION_270");
 
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
     // Ori 270, so width and height swapped
     const Rect physicalFrame{0, 0, DISPLAY_HEIGHT, DISPLAY_WIDTH};
@@ -7195,7 +7195,7 @@
     prepareDisplay(displayRotation);
 
     __attribute__((unused)) SingleTouchInputMapper& mapper =
-            addMapperAndConfigure<SingleTouchInputMapper>();
+            constructAndAddMapper<SingleTouchInputMapper>();
 
     const InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
     // MotionRanges use display pixels as their units
@@ -7240,7 +7240,7 @@
         prepareDisplay(ui::ROTATION_0);
         prepareButtons();
         prepareAxes(POSITION);
-        auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+        auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
 
         mStylusState.when = ARBITRARY_TIME;
         mStylusState.pressure = 0.f;
@@ -7729,7 +7729,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -8013,7 +8013,7 @@
     mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
                                    /*flat*/ 0, /*flat*/ 0, /*resolution*/ 15);
 
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // X and Y axes
     assertAxisResolution(mapper, AMOTION_EVENT_AXIS_X, 10 / X_PRECISION);
@@ -8037,7 +8037,7 @@
 
     // We do not add ABS_MT_TOUCH_MAJOR / MINOR or ABS_MT_WIDTH_MAJOR / MINOR axes
 
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // Touch major and minor
     assertAxisNotPresent(mapper, AMOTION_EVENT_AXIS_TOUCH_MAJOR);
@@ -8052,7 +8052,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -8223,7 +8223,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     prepareVirtualKeys();
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -8388,7 +8388,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -8438,7 +8438,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.size.calibration", "geometric");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -8478,7 +8478,7 @@
     addConfigurationProperty("touch.size.scale", "10");
     addConfigurationProperty("touch.size.bias", "160");
     addConfigurationProperty("touch.size.isSummed", "1");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     // Note: We only provide a single common touch/tool value because the device is assumed
@@ -8528,7 +8528,7 @@
     addConfigurationProperty("touch.size.calibration", "area");
     addConfigurationProperty("touch.size.scale", "43");
     addConfigurationProperty("touch.size.bias", "3");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -8560,7 +8560,7 @@
     prepareAxes(POSITION | PRESSURE);
     addConfigurationProperty("touch.pressure.calibration", "amplitude");
     addConfigurationProperty("touch.pressure.scale", "0.01");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     InputDeviceInfo info;
     mapper.populateDeviceInfo(info);
@@ -8592,7 +8592,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -8835,7 +8835,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0);
@@ -8892,7 +8892,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9043,7 +9043,7 @@
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9113,7 +9113,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9194,7 +9194,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
     mFakePolicy->addInputPortAssociation(usb2, hdmi2);
@@ -9224,7 +9224,7 @@
 TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayUniqueId) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
 
@@ -9253,7 +9253,7 @@
 
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // Check source is mouse that would obtain the PointerController.
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
@@ -9273,7 +9273,7 @@
 TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     prepareDisplay(ui::ROTATION_0);
     process(mapper, 10, /*readTime=*/11, EV_ABS, ABS_MT_TRACKING_ID, 1);
@@ -9304,7 +9304,7 @@
                                     /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -9324,7 +9324,7 @@
                                     /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
     configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -9346,7 +9346,7 @@
 
     configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // Finger down
     int32_t x = 100, y = 100;
@@ -9389,7 +9389,7 @@
     // Setup the first touch screen device.
     prepareAxes(POSITION | ID | SLOT);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
@@ -9413,9 +9413,9 @@
                                             String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper& mapper2 =
-            device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID,
-                                                      mFakePolicy->getReaderConfiguration());
+    device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+    MultiTouchInputMapper& mapper2 = device2->constructAndAddMapper<
+            MultiTouchInputMapper>(SECOND_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
     std::list<NotifyArgs> unused =
             device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                                /*changes=*/{});
@@ -9480,7 +9480,7 @@
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     // Unrotated video frame
@@ -9504,7 +9504,7 @@
 TEST_F(MultiTouchInputMapperTest, VideoFrames_AreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     NotifyMotionArgs motionArgs;
@@ -9529,7 +9529,7 @@
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     addConfigurationProperty("touch.orientationAware", "0");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     NotifyMotionArgs motionArgs;
@@ -9560,7 +9560,7 @@
 TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     // Unrotated video frames. There's no rule that they must all have the same dimensions,
     // so mix these.
     TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
@@ -9583,7 +9583,7 @@
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     addConfigurationProperty("touch.orientationAware", "0");
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     // Unrotated video frames. There's no rule that they must all have the same dimensions,
     // so mix these.
     TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
@@ -9620,7 +9620,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     ASSERT_EQ(mDevice->isEnabled(), false);
 
@@ -9641,7 +9641,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9686,7 +9686,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9734,7 +9734,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9809,7 +9809,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9907,7 +9907,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -9979,7 +9979,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -10036,7 +10036,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // First finger down.
     processId(mapper, FIRST_TRACKING_ID);
@@ -10077,7 +10077,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // First finger touches down and releases.
     processId(mapper, FIRST_TRACKING_ID);
@@ -10105,7 +10105,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
@@ -10160,7 +10160,7 @@
             std::make_shared<FakePointerController>();
     mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setStylusPointerIconEnabled(true);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     processId(mapper, FIRST_TRACKING_ID);
     processPressure(mapper, RAW_PRESSURE_MIN);
@@ -10187,7 +10187,7 @@
             std::make_shared<FakePointerController>();
     mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setStylusPointerIconEnabled(false);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     processId(mapper, FIRST_TRACKING_ID);
     processPressure(mapper, RAW_PRESSURE_MIN);
@@ -10215,7 +10215,7 @@
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 
@@ -10250,7 +10250,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerCapture(true);
     mFakePolicy->setPointerController(fakePointerController);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // captured touchpad should be a touchpad source
     NotifyDeviceResetArgs resetArgs;
@@ -10398,7 +10398,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     // run uncaptured pointer tests - pushes out generic events
     // FINGER 0 DOWN
     processId(mapper, 3);
@@ -10459,7 +10459,7 @@
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
     mFakePolicy->setPointerCapture(false);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // uncaptured touchpad should be a pointer device
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
@@ -10483,7 +10483,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(ui::ROTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     nsecs_t kernelEventTime = ARBITRARY_TIME;
     nsecs_t expectedEventTime = ARBITRARY_TIME;
@@ -10570,7 +10570,7 @@
     // which is greater than fraction of the diagnal length of the touchpad (349).
     // Thus, MaxSwipWidth is 750.
     preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     NotifyMotionArgs motionArgs;
 
     // Two fingers down at once.
@@ -10630,7 +10630,7 @@
     // which is greater than fraction of the diagnal length of the touchpad (349).
     // Thus, MaxSwipWidth is the fraction of the diagnal length, 349.
     preparePointerMode(/*xResolution=*/5, /*yResolution=*/5);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     NotifyMotionArgs motionArgs;
 
     // Two fingers down at once.
@@ -10686,7 +10686,7 @@
  */
 TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) {
     preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -10781,7 +10781,7 @@
 
 TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
     preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     NotifyMotionArgs motionArgs;
 
     // Place two fingers down.
@@ -10828,7 +10828,7 @@
 TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
     preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     // Start a stylus gesture.
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index 84ac0fd..b992928 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -75,9 +75,12 @@
 
     template <class T, typename... Args>
     T& getMapper(Args... args) {
-        T& mapper = mFuzzDevice->addMapper<T>(mFdp->ConsumeIntegral<int32_t>(), args...);
+        int32_t eventhubId = mFdp->ConsumeIntegral<int32_t>();
+        // ensure a device entry exists for this eventHubId
+        mFuzzDevice->addEmptyEventHubDevice(eventhubId);
         configureDevice();
-        return mapper;
+
+        return mFuzzDevice->template constructAndAddMapper<T>(eventhubId, args...);
     }
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 91f945b..06ac8cb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -110,6 +110,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <gui/LayerStatePermissions.h>
 #include <ui/DisplayIdentification.h>
 #include "BackgroundExecutor.h"
 #include "Client.h"
@@ -4392,7 +4393,7 @@
                                       transaction.inputWindowCommands,
                                       transaction.desiredPresentTime, transaction.isAutoTimestamp,
                                       std::move(transaction.uncacheBufferIds), transaction.postTime,
-                                      transaction.permissions, transaction.hasListenerCallbacks,
+                                      transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
     }
@@ -4471,24 +4472,27 @@
 status_t SurfaceFlinger::setTransactionState(
         const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-        const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
-        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-        uint64_t transactionId) {
+        InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
+        const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
-    uint32_t permissions =
-        callingThreadHasUnscopedSurfaceFlingerAccess() ?
-        layer_state_t::Permission::ACCESS_SURFACE_FLINGER : 0;
-    // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
-    // permissions.
-    if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
-        callingThreadHasPermission(sRotateSurfaceFlinger)) {
-        permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int originPid = ipc->getCallingPid();
+    const int originUid = ipc->getCallingUid();
+    uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
+    for (auto composerState : states) {
+        composerState.state.sanitize(permissions);
     }
 
-    if (callingThreadHasPermission(sInternalSystemWindow)) {
-        permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
+    for (DisplayState display : displays) {
+        display.sanitize(permissions);
+    }
+
+    if (!inputWindowCommands.empty() &&
+        (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
+        ALOGE("Only privileged callers are allowed to send input commands.");
+        inputWindowCommands.clear();
     }
 
     if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
@@ -4504,10 +4508,6 @@
 
     const int64_t postTime = systemTime();
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int originPid = ipc->getCallingPid();
-    const int originUid = ipc->getCallingUid();
-
     std::vector<uint64_t> uncacheBufferIds;
     uncacheBufferIds.reserve(uncacheBuffers.size());
     for (const auto& uncacheBuffer : uncacheBuffers) {
@@ -4554,12 +4554,11 @@
                            displays,
                            flags,
                            applyToken,
-                           inputWindowCommands,
+                           std::move(inputWindowCommands),
                            desiredPresentTime,
                            isAutoTimestamp,
                            std::move(uncacheBufferIds),
                            postTime,
-                           permissions,
                            hasListenerCallbacks,
                            listenerCallbacks,
                            originPid,
@@ -4588,14 +4587,12 @@
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
                                            const std::vector<uint64_t>& uncacheBufferIds,
-                                           const int64_t postTime, uint32_t permissions,
-                                           bool hasListenerCallbacks,
+                                           const int64_t postTime, bool hasListenerCallbacks,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
     if (!mLayerLifecycleManagerEnabled) {
         for (DisplayState& display : displays) {
-            display.sanitize(permissions);
             transactionFlags |= setDisplayStateLocked(display);
         }
     }
@@ -4612,12 +4609,12 @@
         if (mLegacyFrontEndEnabled) {
             clientStateFlags |=
                     setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
-                                         isAutoTimestamp, postTime, permissions, transactionId);
+                                         isAutoTimestamp, postTime, transactionId);
 
         } else /*mLayerLifecycleManagerEnabled*/ {
             clientStateFlags |= updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState,
                                                              desiredPresentTime, isAutoTimestamp,
-                                                             postTime, permissions, transactionId);
+                                                             postTime, transactionId);
         }
         if ((flags & eAnimation) && resolvedState.state.surface) {
             if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
@@ -4634,12 +4631,7 @@
     }
 
     transactionFlags |= clientStateFlags;
-
-    if (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) {
-        transactionFlags |= addInputWindowCommands(inputWindowCommands);
-    } else if (!inputWindowCommands.empty()) {
-        ALOGE("Only privileged callers are allowed to send input commands.");
-    }
+    transactionFlags |= addInputWindowCommands(inputWindowCommands);
 
     for (uint64_t uncacheBufferId : uncacheBufferIds) {
         mBufferIdsToUncache.push_back(uncacheBufferId);
@@ -4676,7 +4668,6 @@
     uint32_t transactionFlags = 0;
     for (auto& transaction : transactions) {
         for (DisplayState& display : transaction.displays) {
-            display.sanitize(transaction.permissions);
             transactionFlags |= setDisplayStateLocked(display);
         }
     }
@@ -4775,10 +4766,8 @@
 uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
                                               ResolvedComposerState& composerState,
                                               int64_t desiredPresentTime, bool isAutoTimestamp,
-                                              int64_t postTime, uint32_t permissions,
-                                              uint64_t transactionId) {
+                                              int64_t postTime, uint64_t transactionId) {
     layer_state_t& s = composerState.state;
-    s.sanitize(permissions);
 
     std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
@@ -5127,10 +5116,8 @@
                                                       ResolvedComposerState& composerState,
                                                       int64_t desiredPresentTime,
                                                       bool isAutoTimestamp, int64_t postTime,
-                                                      uint32_t permissions,
                                                       uint64_t transactionId) {
     layer_state_t& s = composerState.state;
-    s.sanitize(permissions);
 
     std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
@@ -5412,7 +5399,6 @@
     const nsecs_t now = systemTime();
     state.desiredPresentTime = now;
     state.postTime = now;
-    state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER;
     state.originPid = mPid;
     state.originUid = static_cast<int>(getuid());
     const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8eaa1c7..d92ec7a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -511,7 +511,7 @@
     status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                  Vector<ComposerState>& state, const Vector<DisplayState>& displays,
                                  uint32_t flags, const sp<IBinder>& applyToken,
-                                 const InputWindowCommands& inputWindowCommands,
+                                 InputWindowCommands inputWindowCommands,
                                  int64_t desiredPresentTime, bool isAutoTimestamp,
                                  const std::vector<client_cache_t>& uncacheBuffers,
                                  bool hasListenerCallbacks,
@@ -732,14 +732,16 @@
     /*
      * Transactions
      */
-    bool applyTransactionState(
-            const FrameTimelineInfo& info, std::vector<ResolvedComposerState>& state,
-            Vector<DisplayState>& displays, uint32_t flags,
-            const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
-            bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds,
-            const int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
-            const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
-            uint64_t transactionId) REQUIRES(mStateLock);
+    bool applyTransactionState(const FrameTimelineInfo& info,
+                               std::vector<ResolvedComposerState>& state,
+                               Vector<DisplayState>& displays, uint32_t flags,
+                               const InputWindowCommands& inputWindowCommands,
+                               const int64_t desiredPresentTime, bool isAutoTimestamp,
+                               const std::vector<uint64_t>& uncacheBufferIds,
+                               const int64_t postTime, bool hasListenerCallbacks,
+                               const std::vector<ListenerCallbacks>& listenerCallbacks,
+                               int originPid, int originUid, uint64_t transactionId)
+            REQUIRES(mStateLock);
     // Flush pending transactions that were presented after desiredPresentTime.
     // For test only
     bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
@@ -760,12 +762,11 @@
 
     uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
-                                  int64_t postTime, uint32_t permissions, uint64_t transactionId)
-            REQUIRES(mStateLock);
+                                  int64_t postTime, uint64_t transactionId) REQUIRES(mStateLock);
     uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
                                           int64_t desiredPresentTime, bool isAutoTimestamp,
-                                          int64_t postTime, uint32_t permissions,
-                                          uint64_t transactionId) REQUIRES(mStateLock);
+                                          int64_t postTime, uint64_t transactionId)
+            REQUIRES(mStateLock);
     uint32_t getTransactionFlags() const;
 
     // Sets the masked bits, and schedules a commit if needed.
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 35c8b6c..62a7dfd 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -54,7 +54,7 @@
                      const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                      const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
                      int64_t desiredPresentTime, bool isAutoTimestamp,
-                     std::vector<uint64_t> uncacheBufferIds, int64_t postTime, uint32_t permissions,
+                     std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
                      bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
                      int originPid, int originUid, uint64_t transactionId)
           : frameTimelineInfo(frameTimelineInfo),
@@ -67,7 +67,6 @@
             isAutoTimestamp(isAutoTimestamp),
             uncacheBufferIds(std::move(uncacheBufferIds)),
             postTime(postTime),
-            permissions(permissions),
             hasListenerCallbacks(hasListenerCallbacks),
             listenerCallbacks(listenerCallbacks),
             originPid(originPid),
@@ -126,7 +125,6 @@
     bool isAutoTimestamp;
     std::vector<uint64_t> uncacheBufferIds;
     int64_t postTime;
-    uint32_t permissions;
     bool hasListenerCallbacks;
     std::vector<ListenerCallbacks> listenerCallbacks;
     int originPid;
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 4a45eb5..69e9a16 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -31,6 +31,7 @@
 #include <utils/String8.h>
 #include <functional>
 #include "utils/ScreenshotUtils.h"
+#include "utils/WindowInfosListenerUtils.h"
 
 namespace android {
 
@@ -378,6 +379,58 @@
     ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
 }
 
+TEST_F(CredentialsTest, TransactionPermissionTest) {
+    WindowInfosListenerUtils windowInfosListenerUtils;
+    std::string name = "Test Layer";
+    sp<IBinder> token = sp<BBinder>::make();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                           ISurfaceComposerClient::eFXSurfaceBufferState);
+    const Rect crop(0, 0, 100, 100);
+    {
+        UIDFaker f(AID_SYSTEM);
+        Transaction()
+                .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
+                .show(surfaceControl)
+                .setLayer(surfaceControl, INT32_MAX - 1)
+                .setCrop(surfaceControl, crop)
+                .setInputWindowInfo(surfaceControl, windowInfo)
+                .apply();
+    }
+
+    // Called from non privileged process
+    Transaction().setTrustedOverlay(surfaceControl, true);
+    {
+        UIDFaker f(AID_SYSTEM);
+        auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
+            auto foundWindowInfo =
+                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+            if (!foundWindowInfo) {
+                return false;
+            }
+            return !foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
+        };
+        windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndNotTrusted);
+    }
+
+    {
+        UIDFaker f(AID_SYSTEM);
+        Transaction().setTrustedOverlay(surfaceControl, true);
+        auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
+            auto foundWindowInfo =
+                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+            if (!foundWindowInfo) {
+                return false;
+            }
+            return foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
+        };
+        windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndTrusted);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 3f27360..ad9a674 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -20,11 +20,13 @@
 #include <private/android_filesystem_config.h>
 #include <cstdint>
 #include <future>
+#include "utils/WindowInfosListenerUtils.h"
 
 namespace android {
 using Transaction = SurfaceComposerClient::Transaction;
 using gui::DisplayInfo;
 using gui::WindowInfo;
+constexpr auto findMatchingWindowInfo = WindowInfosListenerUtils::findMatchingWindowInfo;
 
 using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
 
@@ -37,45 +39,14 @@
 
     void TearDown() override { seteuid(AID_ROOT); }
 
-    struct WindowInfosListener : public gui::WindowInfosListener {
-    public:
-        WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
-              : mPredicate(std::move(predicate)), mPromise(promise) {}
-
-        void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
-            if (mPredicate(update.windowInfos)) {
-                mPromise.set_value();
-            }
-        }
-
-    private:
-        WindowInfosPredicate mPredicate;
-        std::promise<void>& mPromise;
-    };
-
     sp<SurfaceComposerClient> mClient;
+    WindowInfosListenerUtils mWindowInfosListenerUtils;
 
-    bool waitForWindowInfosPredicate(WindowInfosPredicate predicate) {
-        std::promise<void> promise;
-        auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
-        mClient->addWindowInfosListener(listener);
-        auto future = promise.get_future();
-        bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
-        mClient->removeWindowInfosListener(listener);
-        return satisfied;
+    bool waitForWindowInfosPredicate(const WindowInfosPredicate& predicate) {
+        return mWindowInfosListenerUtils.waitForWindowInfosPredicate(std::move(predicate));
     }
 };
 
-const WindowInfo* findMatchingWindowInfo(const WindowInfo& targetWindowInfo,
-                                         const std::vector<WindowInfo>& windowInfos) {
-    for (const WindowInfo& windowInfo : windowInfos) {
-        if (windowInfo.token == targetWindowInfo.token) {
-            return &windowInfo;
-        }
-    }
-    return nullptr;
-}
-
 TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index dbb7c6c..6a641b3 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -386,7 +386,7 @@
                                               transaction.applyToken,
                                               transaction.inputWindowCommands,
                                               transaction.desiredPresentTime,
-                                              transaction.isAutoTimestamp, {}, systemTime(), 0,
+                                              transaction.isAutoTimestamp, {}, systemTime(),
                                               mHasListenerCallbacks, mCallbacks, getpid(),
                                               static_cast<int>(getuid()), transaction.id);
             mFlinger.setTransactionStateInternal(transactionState);
diff --git a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
new file mode 100644
index 0000000..8e28a75
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/android_filesystem_config.h>
+#include <cstdint>
+#include <future>
+
+namespace android {
+using Transaction = SurfaceComposerClient::Transaction;
+using gui::DisplayInfo;
+using gui::WindowInfo;
+
+using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
+
+class WindowInfosListenerUtils {
+public:
+    WindowInfosListenerUtils() { mClient = sp<SurfaceComposerClient>::make(); }
+
+    bool waitForWindowInfosPredicate(const WindowInfosPredicate& predicate) {
+        std::promise<void> promise;
+        auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
+        mClient->addWindowInfosListener(listener);
+        auto future = promise.get_future();
+        bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+        mClient->removeWindowInfosListener(listener);
+        return satisfied;
+    }
+
+    static const WindowInfo* findMatchingWindowInfo(const WindowInfo& targetWindowInfo,
+                                                    const std::vector<WindowInfo>& windowInfos) {
+        for (const WindowInfo& windowInfo : windowInfos) {
+            if (windowInfo.token == targetWindowInfo.token) {
+                return &windowInfo;
+            }
+        }
+        return nullptr;
+    }
+
+private:
+    struct WindowInfosListener : public gui::WindowInfosListener {
+    public:
+        WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
+              : mPredicate(std::move(predicate)), mPromise(promise) {}
+
+        void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+            if (mPredicate(update.windowInfos)) {
+                mPromise.set_value();
+            }
+        }
+
+    private:
+        WindowInfosPredicate mPredicate;
+        std::promise<void>& mPromise;
+    };
+
+    sp<SurfaceComposerClient> mClient;
+};
+
+} // namespace android