Merge "Updating CPP ActivityManager interface to be one way" 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/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index e45a656..aca5009 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -75,6 +75,20 @@
     return DEAD_OBJECT;
 }
 
+status_t ActivityManager::registerUidObserverForUids(const sp<IUidObserver>& observer,
+                                                     const int32_t event, const int32_t cutpoint,
+                                                     const String16& callingPackage,
+                                                     const int32_t uids[], size_t nUids,
+                                                     /*out*/ sp<IBinder>& observerToken) {
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->registerUidObserverForUids(observer, event, cutpoint, callingPackage, uids,
+                                                   nUids, observerToken);
+    }
+    // ActivityManagerService appears dead. Return usual error code for dead service.
+    return DEAD_OBJECT;
+}
+
 status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
 {
     sp<IActivityManager> service = getService();
@@ -85,6 +99,26 @@
     return DEAD_OBJECT;
 }
 
+status_t ActivityManager::addUidToObserver(const sp<IBinder>& observerToken,
+                                           const String16& callingPackage, int32_t uid) {
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->addUidToObserver(observerToken, callingPackage, uid);
+    }
+    // ActivityManagerService appears dead. Return usual error code for dead service.
+    return DEAD_OBJECT;
+}
+
+status_t ActivityManager::removeUidFromObserver(const sp<IBinder>& observerToken,
+                                                const String16& callingPackage, int32_t uid) {
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->removeUidFromObserver(observerToken, callingPackage, uid);
+    }
+    // ActivityManagerService appears dead. Return usual error code for dead service.
+    return DEAD_OBJECT;
+}
+
 bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
 {
     sp<IActivityManager> service = getService();
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 29bbf94..f2b4a6e 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -77,6 +77,30 @@
          return OK;
     }
 
+    virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+                                                const int32_t event, const int32_t cutpoint,
+                                                const String16& callingPackage,
+                                                const int32_t uids[], size_t nUids,
+                                                /*out*/ sp<IBinder>& observerToken) {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeStrongBinder(IInterface::asBinder(observer));
+         data.writeInt32(event);
+         data.writeInt32(cutpoint);
+         data.writeString16(callingPackage);
+         data.writeInt32Array(nUids, uids);
+         status_t err =
+                 remote()->transact(REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION, data, &reply);
+         if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+             return err;
+         }
+         err = reply.readStrongBinder(&observerToken);
+         if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+             return err;
+         }
+         return OK;
+    }
+
     virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer)
     {
          Parcel data, reply;
@@ -89,6 +113,34 @@
          return OK;
     }
 
+    virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+                                      const String16& callingPackage, int32_t uid) {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeStrongBinder(observerToken);
+         data.writeString16(callingPackage);
+         data.writeInt32(uid);
+         status_t err = remote()->transact(ADD_UID_TO_OBSERVER_TRANSACTION, data, &reply);
+         if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+             return err;
+         }
+         return OK;
+    }
+
+    virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+                                           const String16& callingPackage, int32_t uid) {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeStrongBinder(observerToken);
+         data.writeString16(callingPackage);
+         data.writeInt32(uid);
+         status_t err = remote()->transact(REMOVE_UID_FROM_OBSERVER_TRANSACTION, data, &reply);
+         if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+             return err;
+         }
+         return OK;
+    }
+
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
     {
          Parcel data, reply;
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index 5dfbd44..9c634c7 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -82,7 +82,15 @@
                              const int32_t event,
                              const int32_t cutpoint,
                              const String16& callingPackage);
+    status_t registerUidObserverForUids(const sp<IUidObserver>& observer, const int32_t event,
+                                        const int32_t cutpoint, const String16& callingPackage,
+                                        const int32_t uids[], size_t nUids,
+                                        /*out*/ sp<IBinder>& observerToken);
     status_t unregisterUidObserver(const sp<IUidObserver>& observer);
+    status_t addUidToObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+                              int32_t uid);
+    status_t removeUidFromObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+                                   int32_t uid);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
     int getUidProcessState(const uid_t uid, const String16& callingPackage);
     status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
diff --git a/libs/binder/include_activitymanager/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
index 20d12ae..07450c6 100644
--- a/libs/binder/include_activitymanager/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -35,7 +35,16 @@
                                      const int32_t event,
                                      const int32_t cutpoint,
                                      const String16& callingPackage) = 0;
+    virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+                                                const int32_t event, const int32_t cutpoint,
+                                                const String16& callingPackage,
+                                                const int32_t uids[], size_t nUids,
+                                                /*out*/ sp<IBinder>& observerToken) = 0;
     virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+    virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+                                      const String16& callingPackage, int32_t uid) = 0;
+    virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+                                           const String16& callingPackage, int32_t uid) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
     virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
     virtual status_t checkPermission(const String16& permission,
@@ -51,6 +60,9 @@
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
         UNREGISTER_UID_OBSERVER_TRANSACTION,
+        REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION,
+        ADD_UID_TO_OBSERVER_TRANSACTION,
+        REMOVE_UID_FROM_OBSERVER_TRANSACTION,
         IS_UID_ACTIVE_TRANSACTION,
         GET_UID_PROCESS_STATE_TRANSACTION,
         CHECK_PERMISSION_TRANSACTION,
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 33bb343..bf34987 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -217,6 +217,7 @@
         "DebugEGLImageTracker.cpp",
         "DisplayEventDispatcher.cpp",
         "DisplayEventReceiver.cpp",
+        "FenceMonitor.cpp",
         "GLConsumer.cpp",
         "IConsumerListener.cpp",
         "IGraphicBufferConsumer.cpp",
@@ -226,6 +227,7 @@
         "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
         "LayerMetadata.cpp",
+        "LayerStatePermissions.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
diff --git a/libs/gui/FenceMonitor.cpp b/libs/gui/FenceMonitor.cpp
new file mode 100644
index 0000000..230c81a
--- /dev/null
+++ b/libs/gui/FenceMonitor.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/FenceMonitor.h>
+#include <gui/TraceUtils.h>
+#include <log/log.h>
+
+#include <thread>
+
+namespace android::gui {
+
+FenceMonitor::FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
+    std::thread thread(&FenceMonitor::loop, this);
+    pthread_setname_np(thread.native_handle(), mName);
+    thread.detach();
+}
+
+void FenceMonitor::queueFence(const sp<Fence>& fence) {
+    char message[64];
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+        snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
+        ATRACE_NAME(message);
+        // Need an increment on both to make the trace number correct.
+        mFencesQueued++;
+        mFencesSignaled++;
+        return;
+    }
+    snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
+    ATRACE_NAME(message);
+
+    mQueue.push_back(fence);
+    mCondition.notify_one();
+    mFencesQueued++;
+    ATRACE_INT(mName, int32_t(mQueue.size()));
+}
+
+void FenceMonitor::loop() {
+    while (true) {
+        threadLoop();
+    }
+}
+
+void FenceMonitor::threadLoop() {
+    sp<Fence> fence;
+    uint32_t fenceNum;
+    {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (mQueue.empty()) {
+            mCondition.wait(lock);
+        }
+        fence = mQueue[0];
+        fenceNum = mFencesSignaled;
+    }
+    {
+        char message[64];
+        snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
+        ATRACE_NAME(message);
+
+        status_t result = fence->waitForever(message);
+        if (result != OK) {
+            ALOGE("Error waiting for fence: %d", result);
+        }
+    }
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mQueue.pop_front();
+        mFencesSignaled++;
+        ATRACE_INT(mName, int32_t(mQueue.size()));
+    }
+}
+
+} // namespace android::gui
\ No newline at end of file
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/Surface.cpp b/libs/gui/Surface.cpp
index a5cf8d6..ed69100 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -30,6 +30,7 @@
 #include <android/gui/DisplayStatInfo.h>
 #include <android/native_window.h>
 
+#include <gui/FenceMonitor.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
 #include <utils/NativeHandle.h>
@@ -545,82 +546,6 @@
     return NO_ERROR;
 }
 
-class FenceMonitor {
-public:
-    explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
-        std::thread thread(&FenceMonitor::loop, this);
-        pthread_setname_np(thread.native_handle(), mName);
-        thread.detach();
-    }
-
-    void queueFence(const sp<Fence>& fence) {
-        char message[64];
-
-        std::lock_guard<std::mutex> lock(mMutex);
-        if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
-            snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
-            ATRACE_NAME(message);
-            // Need an increment on both to make the trace number correct.
-            mFencesQueued++;
-            mFencesSignaled++;
-            return;
-        }
-        snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
-        ATRACE_NAME(message);
-
-        mQueue.push_back(fence);
-        mCondition.notify_one();
-        mFencesQueued++;
-        ATRACE_INT(mName, int32_t(mQueue.size()));
-    }
-
-private:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
-    void loop() {
-        while (true) {
-            threadLoop();
-        }
-    }
-#pragma clang diagnostic pop
-
-    void threadLoop() {
-        sp<Fence> fence;
-        uint32_t fenceNum;
-        {
-            std::unique_lock<std::mutex> lock(mMutex);
-            while (mQueue.empty()) {
-                mCondition.wait(lock);
-            }
-            fence = mQueue[0];
-            fenceNum = mFencesSignaled;
-        }
-        {
-            char message[64];
-            snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
-            ATRACE_NAME(message);
-
-            status_t result = fence->waitForever(message);
-            if (result != OK) {
-                ALOGE("Error waiting for fence: %d", result);
-            }
-        }
-        {
-            std::lock_guard<std::mutex> lock(mMutex);
-            mQueue.pop_front();
-            mFencesSignaled++;
-            ATRACE_INT(mName, int32_t(mQueue.size()));
-        }
-    }
-
-    const char* mName;
-    uint32_t mFencesQueued;
-    uint32_t mFencesSignaled;
-    std::deque<sp<Fence>> mQueue;
-    std::condition_variable mCondition;
-    std::mutex mMutex;
-};
-
 void Surface::getDequeueBufferInputLocked(
         IGraphicBufferProducer::DequeueBufferInput* dequeueInput) {
     LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null");
@@ -694,7 +619,7 @@
     ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
 
     if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
-        static FenceMonitor hwcReleaseThread("HWC release");
+        static gui::FenceMonitor hwcReleaseThread("HWC release");
         hwcReleaseThread.queueFence(fence);
     }
 
@@ -893,7 +818,7 @@
         sp<GraphicBuffer>& gbuf(mSlots[slot].buffer);
 
         if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
-            static FenceMonitor hwcReleaseThread("HWC release");
+            static gui::FenceMonitor hwcReleaseThread("HWC release");
             hwcReleaseThread.queueFence(output.fence);
         }
 
@@ -1163,7 +1088,7 @@
     mQueueBufferCondition.broadcast();
 
     if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
-        static FenceMonitor gpuCompletionThread("GPU completion");
+        static gui::FenceMonitor gpuCompletionThread("GPU completion");
         gpuCompletionThread.queueFence(fence);
     }
 }
@@ -1871,12 +1796,14 @@
     auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
     auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
     auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
+    auto useForRefreshRateSelection = static_cast<bool>(va_arg(args, int32_t));
 
     ALOGV("Surface::%s", __func__);
     FrameTimelineInfo ftlInfo;
     ftlInfo.vsyncId = frameTimelineVsyncId;
     ftlInfo.inputEventId = inputEventId;
     ftlInfo.startTimeNanos = startTimeNanos;
+    ftlInfo.useForRefreshRateSelection = useForRefreshRateSelection;
     return setFrameTimelineInfo(frameNumber, ftlInfo);
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index eb5cc4f..c6c7367 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>
@@ -2239,11 +2245,13 @@
             t.vsyncId = other.vsyncId;
             t.inputEventId = other.inputEventId;
             t.startTimeNanos = other.startTimeNanos;
+            t.useForRefreshRateSelection = other.useForRefreshRateSelection;
         }
     } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
         t.vsyncId = other.vsyncId;
         t.inputEventId = other.inputEventId;
         t.startTimeNanos = other.startTimeNanos;
+        t.useForRefreshRateSelection = other.useForRefreshRateSelection;
     }
 }
 
@@ -2252,6 +2260,7 @@
     t.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
     t.inputEventId = os::IInputConstants::INVALID_INPUT_EVENT_ID;
     t.startTimeNanos = 0;
+    t.useForRefreshRateSelection = false;
 }
 
 SurfaceComposerClient::Transaction&
diff --git a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
index 6ffe466..6a86c6a 100644
--- a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
+++ b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
@@ -33,4 +33,8 @@
 
     // The current time in nanoseconds the application started to render the frame.
     long startTimeNanos = 0;
+
+    // Whether this vsyncId should be used to heuristically select the display refresh rate
+    // TODO(b/281695725): Clean this up once TextureView use setFrameRate API
+    boolean useForRefreshRateSelection = false;
 }
diff --git a/libs/gui/include/gui/FenceMonitor.h b/libs/gui/include/gui/FenceMonitor.h
new file mode 100644
index 0000000..62cedde
--- /dev/null
+++ b/libs/gui/include/gui/FenceMonitor.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <deque>
+#include <mutex>
+
+#include <ui/Fence.h>
+
+namespace android::gui {
+
+class FenceMonitor {
+public:
+    explicit FenceMonitor(const char* name);
+    void queueFence(const sp<Fence>& fence);
+
+private:
+    void loop();
+    void threadLoop();
+
+    const char* mName;
+    uint32_t mFencesQueued;
+    uint32_t mFencesSignaled;
+    std::deque<sp<Fence>> mQueue;
+    std::condition_variable mCondition;
+    std::mutex mMutex;
+};
+
+} // namespace android::gui
\ No newline at end of file
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/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index af9ce3a..9a459b1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -49,11 +49,10 @@
     std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(eventTime);
     std::chrono::microseconds microseconds =
             std::chrono::duration_cast<std::chrono::microseconds>(eventTime - seconds);
-    struct input_event ev = {.type = type,
-                             .code = code,
-                             .value = value,
-                             .input_event_sec = static_cast<time_t>(seconds.count()),
-                             .input_event_usec = static_cast<suseconds_t>(microseconds.count())};
+    struct input_event ev = {.type = type, .code = code, .value = value};
+    ev.input_event_sec = static_cast<decltype(ev.input_event_sec)>(seconds.count());
+    ev.input_event_usec = static_cast<decltype(ev.input_event_usec)>(microseconds.count());
+
     return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
 }
 
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 6c54635..0fee3c1 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1066,13 +1066,12 @@
                            (int)compatibility, (int)changeFrameRateStrategy);
 }
 
-static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
-                                                        uint64_t frameNumber,
-                                                        int64_t frameTimelineVsyncId,
-                                                        int32_t inputEventId,
-                                                        int64_t startTimeNanos) {
+static inline int native_window_set_frame_timeline_info(
+        struct ANativeWindow* window, uint64_t frameNumber, int64_t frameTimelineVsyncId,
+        int32_t inputEventId, int64_t startTimeNanos, int32_t useForRefreshRateSelection) {
     return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
-                           frameTimelineVsyncId, inputEventId, startTimeNanos);
+                           frameTimelineVsyncId, inputEventId, startTimeNanos,
+                           useForRefreshRateSelection);
 }
 
 // ------------------------------------------------------------------------------------------------
diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
index baa9d75..6959274 100644
--- a/libs/permission/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -146,6 +146,14 @@
     }
 }
 
+void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
+        const sp<IAppOpsCallback>& callback) {
+    sp<IAppOpsService> service = getService();
+    if (service != nullptr) {
+        service->startWatchingModeWithFlags(op, packageName, flags, callback);
+    }
+}
+
 void AppOpsManager::stopWatchingMode(const sp<IAppOpsCallback>& callback) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
index d59f445..7f235a4 100644
--- a/libs/permission/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -166,6 +166,17 @@
         }
         return reply.readBool();
     }
+
+    virtual void startWatchingModeWithFlags(int32_t op, const String16& packageName,
+        int32_t flags, const sp<IAppOpsCallback>& callback) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+        data.writeInt32(op);
+        data.writeString16(packageName);
+        data.writeInt32(flags);
+        data.writeStrongBinder(IInterface::asBinder(callback));
+        remote()->transact(START_WATCHING_MODE_WITH_FLAGS_TRANSACTION, data, &reply);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService")
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index abcd527..243532b 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -151,6 +151,10 @@
         _NUM_OP = 117
     };
 
+    enum {
+        WATCH_FOREGROUND_CHANGES = 1 << 0
+    };
+
     AppOpsManager();
 
     int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
@@ -174,6 +178,8 @@
             const std::optional<String16>& attributionTag);
     void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback);
+    void startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
+            const sp<IAppOpsCallback>& callback);
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
     void setCameraAudioRestriction(int32_t mode);
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index 22f056b..918fcdb 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -52,6 +52,8 @@
             const String16& packageName) = 0;
     virtual void setCameraAudioRestriction(int32_t mode) = 0;
     virtual bool shouldCollectNotes(int32_t opCode) = 0;
+    virtual void startWatchingModeWithFlags(int32_t op, const String16& packageName,
+            int32_t flags, const sp<IAppOpsCallback>& callback) = 0;
 
     enum {
         CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -64,6 +66,7 @@
         CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
         SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
         SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
+        START_WATCHING_MODE_WITH_FLAGS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
     };
 
     enum {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index cfea85f..5854135 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -39,10 +39,10 @@
 #include <SkPath.h>
 #include <SkPoint.h>
 #include <SkPoint3.h>
+#include <SkRRect.h>
 #include <SkRect.h>
 #include <SkRefCnt.h>
 #include <SkRegion.h>
-#include <SkRRect.h>
 #include <SkRuntimeEffect.h>
 #include <SkSamplingOptions.h>
 #include <SkScalar.h>
@@ -51,9 +51,11 @@
 #include <SkString.h>
 #include <SkSurface.h>
 #include <SkTileMode.h>
-#include <src/core/SkTraceEventCommon.h>
 #include <android-base/stringprintf.h>
+#include <gui/FenceMonitor.h>
 #include <gui/TraceUtils.h>
+#include <pthread.h>
+#include <src/core/SkTraceEventCommon.h>
 #include <sync/sync.h>
 #include <ui/BlurRegion.h>
 #include <ui/DataspaceUtils.h>
@@ -63,6 +65,7 @@
 
 #include <cmath>
 #include <cstdint>
+#include <deque>
 #include <memory>
 #include <numeric>
 
@@ -229,7 +232,6 @@
 static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
     return SkPoint3::Make(vector.x, vector.y, vector.z);
 }
-
 } // namespace
 
 namespace android {
@@ -1134,8 +1136,13 @@
         activeSurface->flush();
     }
 
-    base::unique_fd drawFence = flushAndSubmit(grContext);
-    resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
+    auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
+
+    if (ATRACE_ENABLED()) {
+        static gui::FenceMonitor sMonitor("RE Completion");
+        sMonitor.queueFence(drawFence);
+    }
+    resultPromise->set_value(std::move(drawFence));
 }
 
 size_t SkiaRenderEngine::getMaxTextureSize() const {
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..3f6d557 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
@@ -446,17 +446,17 @@
 
     // Switch-like devices.
     if (classes.test(InputDeviceClass::SWITCH)) {
-        mappers.push_back(std::make_unique<SwitchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<SwitchInputMapper>(contextPtr, readerConfig));
     }
 
     // Scroll wheel-like devices.
     if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
-        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<RotaryEncoderInputMapper>(contextPtr, readerConfig));
     }
 
     // Vibrator-like devices.
     if (classes.test(InputDeviceClass::VIBRATOR)) {
-        mappers.push_back(std::make_unique<VibratorInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<VibratorInputMapper>(contextPtr, readerConfig));
     }
 
     // Battery-like devices or light-containing devices.
@@ -488,7 +488,7 @@
 
     // Cursor-like devices.
     if (classes.test(InputDeviceClass::CURSOR)) {
-        mappers.push_back(std::make_unique<CursorInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<CursorInputMapper>(contextPtr, readerConfig));
     }
 
     // Touchscreens and touchpad devices.
@@ -501,26 +501,26 @@
             (identifier.product == 0x05c4 || identifier.product == 0x09cc);
     if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
         classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
-        mappers.push_back(std::make_unique<TouchpadInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<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.
     if (classes.test(InputDeviceClass::JOYSTICK)) {
-        mappers.push_back(std::make_unique<JoystickInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<JoystickInputMapper>(contextPtr, readerConfig));
     }
 
     // Motion sensor enabled devices.
     if (classes.test(InputDeviceClass::SENSOR)) {
-        mappers.push_back(std::make_unique<SensorInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(createInputMapper<SensorInputMapper>(contextPtr, readerConfig));
     }
 
     // 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/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index dab4661..061c6a3 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -199,6 +199,29 @@
         }
     }
 
+    // Send BUTTON_RELEASE events. (This has to happen before any UP events to avoid sending
+    // BUTTON_RELEASE events without any pointers.)
+    uint32_t newButtonState;
+    if (coords.size() - upSlots.size() + downSlots.size() == 0) {
+        // If there won't be any pointers down after this evdev sync, we won't be able to send
+        // button updates on their own, as motion events without pointers are invalid. To avoid
+        // erroneously reporting buttons being held for long periods, send BUTTON_RELEASE events for
+        // all pressed buttons when the last pointer is lifted.
+        //
+        // This also prevents us from sending BUTTON_PRESS events too early in the case of touchpads
+        // which report a button press one evdev sync before reporting a touch going down.
+        newButtonState = 0;
+    } else {
+        newButtonState = mCursorButtonAccumulator.getButtonState();
+    }
+    for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
+        if (!(newButtonState & button) && mButtonState & button) {
+            mButtonState &= ~button;
+            out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+                                         coords, properties, /*actionButton=*/button));
+        }
+    }
+
     // For any touches that were lifted, send UP or POINTER_UP events.
     for (size_t slotNumber : upSlots) {
         const size_t indexToRemove = coordsIndexForSlotNumber.at(slotNumber);
@@ -240,16 +263,11 @@
         out.push_back(makeMotionArgs(when, readTime, action, coords, properties));
     }
 
-    const uint32_t newButtonState = mCursorButtonAccumulator.getButtonState();
     for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
         if (newButtonState & button && !(mButtonState & button)) {
             mButtonState |= button;
             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, coords,
                                          properties, /*actionButton=*/button));
-        } else if (!(newButtonState & button) && mButtonState & button) {
-            mButtonState &= ~button;
-            out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                                         coords, properties, /*actionButton=*/button));
         }
     }
     return out;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 8ef5ff6..c684ed4 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -71,9 +71,7 @@
 CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
                                      const InputReaderConfiguration& readerConfig)
       : InputMapper(deviceContext, readerConfig),
-        mLastEventTime(std::numeric_limits<nsecs_t>::min()) {
-    configureWithZeroChanges(readerConfig);
-}
+        mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
 
 CursorInputMapper::~CursorInputMapper() {
     if (mPointerController != nullptr) {
@@ -142,46 +140,51 @@
                                                      ConfigurationChanges changes) {
     std::list<NotifyArgs> out = InputMapper::reconfigure(when, readerConfig, changes);
 
-    if (!changes.any()) {
-        configureWithZeroChanges(readerConfig);
-        return out;
+    if (!changes.any()) { // first time only
+        configureBasicParams();
     }
 
-    const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
-            changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE);
+    const bool configurePointerCapture = !changes.any() ||
+            (mParameters.mode != Parameters::Mode::NAVIGATION &&
+             changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE));
     if (configurePointerCapture) {
         configureOnPointerCapture(readerConfig);
         out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
     }
 
-    if (changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || configurePointerCapture) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
+        configurePointerCapture) {
         configureOnChangePointerSpeed(readerConfig);
     }
 
-    if (changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
+    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) ||
+        configurePointerCapture) {
         configureOnChangeDisplayInfo(readerConfig);
     }
     return out;
 }
 
-void CursorInputMapper::configureParameters() {
-    mParameters.mode = Parameters::Mode::POINTER;
-    const PropertyMap& config = getDeviceContext().getConfiguration();
+CursorInputMapper::Parameters CursorInputMapper::computeParameters(
+        const InputDeviceContext& deviceContext) {
+    Parameters parameters;
+    parameters.mode = Parameters::Mode::POINTER;
+    const PropertyMap& config = deviceContext.getConfiguration();
     std::optional<std::string> cursorModeString = config.getString("cursor.mode");
     if (cursorModeString.has_value()) {
         if (*cursorModeString == "navigation") {
-            mParameters.mode = Parameters::Mode::NAVIGATION;
+            parameters.mode = Parameters::Mode::NAVIGATION;
         } else if (*cursorModeString != "pointer" && *cursorModeString != "default") {
             ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString->c_str());
         }
     }
 
-    mParameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
+    parameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
 
-    mParameters.hasAssociatedDisplay = false;
-    if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
-        mParameters.hasAssociatedDisplay = true;
+    parameters.hasAssociatedDisplay = false;
+    if (parameters.mode == Parameters::Mode::POINTER || parameters.orientationAware) {
+        parameters.hasAssociatedDisplay = true;
     }
+    return parameters;
 }
 
 void CursorInputMapper::dumpParameters(std::string& dump) {
@@ -424,22 +427,11 @@
     return mDisplayId;
 }
 
-void CursorInputMapper::configureWithZeroChanges(const InputReaderConfiguration& readerConfig) {
-    // Configuration with zero changes
-    configureBasicParams();
-    if (mParameters.mode != Parameters::Mode::NAVIGATION &&
-        readerConfig.pointerCaptureRequest.enable) {
-        configureOnPointerCapture(readerConfig);
-    }
-    configureOnChangePointerSpeed(readerConfig);
-    configureOnChangeDisplayInfo(readerConfig);
-}
-
 void CursorInputMapper::configureBasicParams() {
     mCursorScrollAccumulator.configure(getDeviceContext());
 
     // Configure basic parameters.
-    configureParameters();
+    mParameters = computeParameters(getDeviceContext());
 
     // Configure device mode.
     switch (mParameters.mode) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index caf2e5a..b879bfd 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -53,8 +53,10 @@
 
 class CursorInputMapper : public InputMapper {
 public:
-    explicit CursorInputMapper(InputDeviceContext& deviceContext,
-                               const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~CursorInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -125,15 +127,17 @@
     nsecs_t mDownTime;
     nsecs_t mLastEventTime;
 
-    void configureParameters();
+    explicit CursorInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
     void dumpParameters(std::string& dump);
-    void configureWithZeroChanges(const InputReaderConfiguration& readerConfig);
     void configureBasicParams();
     void configureOnPointerCapture(const InputReaderConfiguration& config);
     void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
     void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
 
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+
+    static Parameters computeParameters(const InputDeviceContext& deviceContext);
 };
 
 } // namespace android
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/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 49673a2..313f092 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -22,8 +22,10 @@
 
 class JoystickInputMapper : public InputMapper {
 public:
-    explicit JoystickInputMapper(InputDeviceContext& deviceContext,
-                                 const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~JoystickInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -87,6 +89,9 @@
         }
     };
 
+    explicit JoystickInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
+
     static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
                            bool explicitlyMapped);
 
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/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index d3dcbe1..9e2e8c4 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -25,8 +25,10 @@
 
 class RotaryEncoderInputMapper : public InputMapper {
 public:
-    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
-                                      const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~RotaryEncoderInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -45,6 +47,8 @@
     float mScalingFactor;
     ui::Rotation mOrientation;
 
+    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+                                      const InputReaderConfiguration& readerConfig);
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
 
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 1f82559..a55dcd1 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -27,8 +27,10 @@
 
 class SensorInputMapper : public InputMapper {
 public:
-    explicit SensorInputMapper(InputDeviceContext& deviceContext,
-                               const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     ~SensorInputMapper() override;
 
     uint32_t getSources() const override;
@@ -106,6 +108,9 @@
         }
     };
 
+    explicit SensorInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
+
     static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo);
 
     // Axes indexed by raw ABS_* axis index.
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/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 7ec282b..2fb48bb 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -22,8 +22,10 @@
 
 class SwitchInputMapper : public InputMapper {
 public:
-    explicit SwitchInputMapper(InputDeviceContext& deviceContext,
-                               const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -36,6 +38,8 @@
     uint32_t mSwitchValues;
     uint32_t mUpdatedSwitchMask;
 
+    explicit SwitchInputMapper(InputDeviceContext& deviceContext,
+                               const InputReaderConfiguration& readerConfig);
     void processSwitch(int32_t switchCode, int32_t switchValue);
     [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
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/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index a5da3cd..c72425a 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -270,6 +270,14 @@
         accelCurveProp.setRealValues(
                 createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
                                                       accelCurveProp.getCount()));
+        mPropertyProvider.getProperty("Use Custom Touchpad Scroll Accel Curve")
+                .setBoolValues({true});
+        GesturesProp scrollCurveProp = mPropertyProvider.getProperty("Scroll Accel Curve");
+        scrollCurveProp.setRealValues(
+                createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
+                                                      scrollCurveProp.getCount()));
+        mPropertyProvider.getProperty("Scroll X Out Scale").setRealValues({1.0});
+        mPropertyProvider.getProperty("Scroll Y Out Scale").setRealValues({1.0});
         mPropertyProvider.getProperty("Invert Scrolling")
                 .setBoolValues({config.touchpadNaturalScrollingEnabled});
         mPropertyProvider.getProperty("Tap Enable")
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 3128d18..23d0fd3 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -40,8 +40,10 @@
 
 class TouchpadInputMapper : public InputMapper {
 public:
-    explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
-                                 const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     ~TouchpadInputMapper();
 
     uint32_t getSources() const override;
@@ -58,6 +60,8 @@
 
 private:
     void resetGestureInterpreter(nsecs_t when);
+    explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
     [[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                           SelfContainedHardwareState schs);
     [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 384c075..9079c73 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -22,8 +22,10 @@
 
 class VibratorInputMapper : public InputMapper {
 public:
-    explicit VibratorInputMapper(InputDeviceContext& deviceContext,
-                                 const InputReaderConfiguration& readerConfig);
+    template <class T, class... Args>
+    friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+                                                const InputReaderConfiguration& readerConfig,
+                                                Args... args);
     virtual ~VibratorInputMapper();
 
     virtual uint32_t getSources() const override;
@@ -46,6 +48,8 @@
     ssize_t mIndex;
     nsecs_t mNextStepTime;
 
+    explicit VibratorInputMapper(InputDeviceContext& deviceContext,
+                                 const InputReaderConfiguration& readerConfig);
     [[nodiscard]] std::list<NotifyArgs> nextStep();
     [[nodiscard]] NotifyVibratorStateArgs stopVibrating();
 };
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 3dc5152..99a6a1f 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -781,4 +781,175 @@
                       WithPointerId(/*index=*/1, /*id=*/0)));
 }
 
+// Motion events without any pointers are invalid, so when a button press is reported in the same
+// frame as a touch down, the button press must be reported second. Similarly with a button release
+// and a touch lift.
+TEST_F(CapturedTouchpadEventConverterTest,
+       ButtonPressedAndReleasedInSameFrameAsTouch_ReportedWithPointers) {
+    CapturedTouchpadEventConverter conv = createConverter();
+
+    processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 1);
+
+    std::list<NotifyArgs> args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
+                      WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 0);
+    args = processSync(conv);
+    ASSERT_EQ(3u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
+                      WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(0)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_UP));
+}
+
+// Some touchpads sometimes report a button press before they report the finger touching the pad. In
+// that case we need to wait until the touch comes to report the button press.
+TEST_F(CapturedTouchpadEventConverterTest, ButtonPressedBeforeTouch_ReportedOnceTouchOccurs) {
+    CapturedTouchpadEventConverter conv = createConverter();
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 1);
+    ASSERT_EQ(0u, processSync(conv).size());
+
+    processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+    std::list<NotifyArgs> args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
+                      WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+}
+
+// When all fingers are lifted from a touchpad, we should release any buttons that are down, since
+// we won't be able to report them being lifted later if no pointers are present.
+TEST_F(CapturedTouchpadEventConverterTest, ButtonReleasedAfterTouchLifts_ReportedWithLift) {
+    CapturedTouchpadEventConverter conv = createConverter();
+
+    processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 1);
+
+    std::list<NotifyArgs> args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+    args = processSync(conv);
+    ASSERT_EQ(3u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
+                      WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(0)));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_UP));
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 0);
+    ASSERT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, MultipleButtonsPressedDuringTouch_ReportedCorrectly) {
+    CapturedTouchpadEventConverter conv = createConverter();
+
+    processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+    processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+    processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+    processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+    processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+    EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+                WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 1);
+    std::list<NotifyArgs> args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                      WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+
+    processAxis(conv, EV_KEY, BTN_RIGHT, 1);
+    args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                      WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+                                      AMOTION_EVENT_BUTTON_SECONDARY)));
+
+    processAxis(conv, EV_KEY, BTN_LEFT, 0);
+    args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                      WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+                      WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY)));
+
+    processAxis(conv, EV_KEY, BTN_RIGHT, 0);
+    args = processSync(conv);
+    ASSERT_EQ(2u, args.size());
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+    args.pop_front();
+    EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                      WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0)));
+}
+
 } // namespace android
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..9fbe762 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2603,13 +2603,13 @@
 };
 
 TEST_F(SwitchInputMapperTest, GetSources) {
-    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+    SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
 
     ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
 }
 
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
-    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+    SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
 
     mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
     ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
@@ -2619,7 +2619,7 @@
 }
 
 TEST_F(SwitchInputMapperTest, Process) {
-    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+    SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
     std::list<NotifyArgs> out;
     out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
     ASSERT_TRUE(out.empty());
@@ -2645,13 +2645,13 @@
 };
 
 TEST_F(VibratorInputMapperTest, GetSources) {
-    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+    VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
 }
 
 TEST_F(VibratorInputMapperTest, GetVibratorIds) {
-    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+    VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
 
     ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
 }
@@ -2659,7 +2659,7 @@
 TEST_F(VibratorInputMapperTest, Vibrate) {
     constexpr uint8_t DEFAULT_AMPLITUDE = 192;
     constexpr int32_t VIBRATION_TOKEN = 100;
-    VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+    VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
 
     VibrationElement pattern(2);
     VibrationSequence sequence(2);
@@ -2784,7 +2784,7 @@
 }
 
 TEST_F(SensorInputMapperTest, GetSources) {
-    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
 
     ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
 }
@@ -2792,7 +2792,7 @@
 TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
     setAccelProperties();
     prepareAccelAxes();
-    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
 
     ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
                                     std::chrono::microseconds(10000),
@@ -2822,7 +2822,7 @@
 TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
     setGyroProperties();
     prepareGyroAxes();
-    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+    SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
 
     ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
                                     std::chrono::microseconds(10000),
@@ -3862,21 +3862,21 @@
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     InputDeviceInfo info;
     mapper.populateDeviceInfo(info);
@@ -3906,7 +3906,7 @@
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     InputDeviceInfo info;
     mapper.populateDeviceInfo(info);
@@ -3924,7 +3924,7 @@
 
 TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4012,7 +4012,7 @@
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4036,7 +4036,7 @@
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4065,7 +4065,7 @@
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
     addConfigurationProperty("cursor.mode", "navigation");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4114,7 +4114,7 @@
     // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
     // need to be rotated.
     addConfigurationProperty("cursor.orientationAware", "1");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     prepareDisplay(ui::ROTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -4132,7 +4132,7 @@
     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>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     clearViewports();
     prepareDisplay(ui::ROTATION_0);
@@ -4181,7 +4181,7 @@
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -4435,7 +4435,7 @@
 
 TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -4456,7 +4456,7 @@
 TEST_F(CursorInputMapperTest, Process_PointerCapture) {
     addConfigurationProperty("cursor.mode", "pointer");
     mFakePolicy->setPointerCapture(true);
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4548,7 +4548,7 @@
     const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f,
                                                /*high threshold=*/100.f, /*acceleration=*/10.f);
     mFakePolicy->setVelocityControlParams(testParams);
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4589,7 +4589,7 @@
 
 TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4630,7 +4630,7 @@
 }
 
 TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     // Set up the default display.
     prepareDisplay(ui::ROTATION_90);
@@ -4656,7 +4656,7 @@
 }
 
 TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     // Set up the default display.
     prepareDisplay(ui::ROTATION_90);
@@ -4682,7 +4682,7 @@
 }
 
 TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     // Set up the default display as the display on which the pointer should be shown.
     prepareDisplay(ui::ROTATION_90);
@@ -4715,7 +4715,7 @@
 
 TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     nsecs_t kernelEventTime = ARBITRARY_TIME;
     nsecs_t expectedEventTime = ARBITRARY_TIME;
@@ -4742,7 +4742,7 @@
 
 TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     nsecs_t expectedEventTime = ARBITRARY_TIME;
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
@@ -4779,7 +4779,7 @@
 
 TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) {
     addConfigurationProperty("cursor.mode", "pointer");
-    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
 
     nsecs_t kernelEventTime = ARBITRARY_TIME;
     nsecs_t expectedEventTime = ARBITRARY_TIME;
@@ -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.
@@ -10904,7 +10904,7 @@
 
 TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
     prepareAxes();
-    JoystickInputMapper& mapper = addMapperAndConfigure<JoystickInputMapper>();
+    JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>();
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
 
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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 20f4de1..f6ca9e2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -50,8 +50,6 @@
 
 namespace hal = hardware::graphics::composer::hal;
 
-ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
-
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
         const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
         std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -282,10 +280,6 @@
                                   Rect orientedDisplaySpaceRect) {
     mOrientation = orientation;
 
-    if (isPrimary()) {
-        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
-    }
-
     // We need to take care of display rotation for globalTransform for case if the panel is not
     // installed aligned with device orientation.
     const auto transformOrientation = orientation + mPhysicalOrientation;
@@ -326,10 +320,6 @@
     return mStagedBrightness;
 }
 
-ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
-    return sPrimaryDisplayRotationFlags;
-}
-
 void DisplayDevice::dump(utils::Dumper& dumper) const {
     using namespace std::string_view_literals;
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 51876e7..dc5f8a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -109,8 +109,6 @@
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
     ui::Rotation getOrientation() const { return mOrientation; }
 
-    static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
-
     std::optional<float> getStagedBrightness() const REQUIRES(kMainThreadContext);
     ui::Transform::RotationFlags getTransformHint() const;
     const ui::Transform& getTransform() const;
@@ -274,8 +272,6 @@
     const ui::Rotation mPhysicalOrientation;
     ui::Rotation mOrientation = ui::ROTATION_0;
 
-    static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
-
     // Allow nullopt as initial power mode.
     using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
     std::optional<TracedPowerMode> mPowerMode;
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 76b36fe..218a64a 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -28,7 +28,7 @@
     ui::Transform transform;
     bool receivesInput;
     bool isSecure;
-    // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
+    // TODO(b/259407931): can eliminate once SurfaceFlinger::sActiveDisplayRotationFlags is removed.
     bool isPrimary;
     bool isVirtual;
     ui::Transform::RotationFlags rotationFlags;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ce7d37e..985c6f9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -667,6 +667,7 @@
     snapshot.relativeLayerMetadata.mMap.clear();
 }
 
+// TODO (b/259407931): Remove.
 uint32_t getPrimaryDisplayRotationFlags(
         const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
     for (auto& [_, display] : displays) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index bfb7a22..f12aab7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1650,8 +1650,8 @@
 
 void Layer::dumpOffscreenDebugInfo(std::string& result) const {
     std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
-    StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
-                  mOwnerPid, mOwnerUid);
+    StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(),
+                  mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : "");
 }
 
 void Layer::onDisconnect() {
@@ -2985,7 +2985,7 @@
     if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
         std::swap(bufferWidth, bufferHeight);
     }
-    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+    uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
     if (mDrawingState.transformToDisplayInverse) {
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufferWidth, bufferHeight);
@@ -3085,6 +3085,7 @@
     mDrawingState.desiredPresentTime = desiredPresentTime;
     mDrawingState.isAutoTimestamp = isAutoTimestamp;
     mDrawingState.latchedVsyncId = info.vsyncId;
+    mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection;
     mDrawingState.modified = true;
     if (!buffer) {
         resetDrawingStateBufferInfo();
@@ -3147,15 +3148,31 @@
 }
 
 void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+    ATRACE_CALL();
     const nsecs_t presentTime = [&] {
-        if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime;
+        if (!mDrawingState.isAutoTimestamp) {
+            ATRACE_FORMAT_INSTANT("desiredPresentTime");
+            return mDrawingState.desiredPresentTime;
+        }
 
-        const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
-                mDrawingState.latchedVsyncId);
-        if (prediction.has_value()) return prediction->presentTime;
+        if (mDrawingState.useVsyncIdForRefreshRateSelection) {
+            const auto prediction =
+                    mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                            mDrawingState.latchedVsyncId);
+            if (prediction.has_value()) {
+                ATRACE_FORMAT_INSTANT("predictedPresentTime");
+                return prediction->presentTime;
+            }
+        }
 
         return static_cast<nsecs_t>(0);
     }();
+
+    if (ATRACE_ENABLED() && presentTime > 0) {
+        const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now();
+        ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
+    }
+
     mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
                                              scheduler::LayerHistory::LayerUpdateType::Buffer);
 }
@@ -3295,7 +3312,7 @@
     }
 
     if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4374914..38590e6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -234,6 +234,7 @@
         float desiredHdrSdrRatio = 1.f;
         gui::CachingHint cachingHint = gui::CachingHint::Enabled;
         int64_t latchedVsyncId = 0;
+        bool useVsyncIdForRefreshRateSelection = false;
     };
 
     explicit Layer(const LayerCreationArgs& args);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index e713263..f855f27 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -25,8 +25,8 @@
 #include <system/window.h>
 #include <utils/Log.h>
 
-#include "DisplayDevice.h"
 #include "LayerFE.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
@@ -260,7 +260,7 @@
          * the code below applies the primary display's inverse transform to
          * the texture transform
          */
-        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        uint32_t transform = SurfaceFlinger::getActiveDisplayRotationFlags();
         mat4 tr = inverseOrientation(transform);
 
         /**
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2d40678..b05838b 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"
@@ -355,6 +356,8 @@
             PermissionCache::checkPermission(permission, pid, uid);
 }
 
+ui::Transform::RotationFlags SurfaceFlinger::sActiveDisplayRotationFlags = ui::Transform::ROT_0;
+
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mPid(getpid()),
@@ -2618,7 +2621,7 @@
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
-    refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
+    refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags();
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -3562,6 +3565,8 @@
                                    currentState.orientedDisplaySpaceRect);
             if (display->getId() == mActiveDisplayId) {
                 mActiveDisplayTransformHint = display->getTransformHint();
+                sActiveDisplayRotationFlags =
+                        ui::Transform::toRotationFlags(display->getOrientation());
             }
         }
         if (currentState.width != drawingState.width ||
@@ -4031,6 +4036,9 @@
         std::vector<Layer*> needsUpdating;
         for (Layer* cloneRoot : mLayerMirrorRoots) {
             pendingUpdates.pop_front();
+            if (cloneRoot->isRemovedFromCurrentState()) {
+                continue;
+            }
             if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
             } else {
                 needsUpdating.push_back(cloneRoot);
@@ -4170,8 +4178,8 @@
                     leakingParentLayerFound = true;
                     sp<Layer> parent = sp<Layer>::fromExisting(layer);
                     while (parent) {
-                        ALOGE("Parent Layer: %s handleIsAlive: %s", parent->getName().c_str(),
-                              std::to_string(parent->isHandleAlive()).c_str());
+                        ALOGE("Parent Layer: %s%s", parent->getName().c_str(),
+                              (parent->isHandleAlive() ? "handleAlive" : ""));
                         parent = parent->getParent();
                     }
                     // Sample up to 100 layers
@@ -4186,21 +4194,28 @@
                 }
             });
 
-            ALOGE("Dumping random sampling of on-screen layers: ");
+            int numLayers = 0;
+            mDrawingState.traverse([&](Layer* layer) { numLayers++; });
+
+            ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers);
             mDrawingState.traverse([&](Layer* layer) {
                 // Aim to dump about 200 layers to avoid totally trashing
                 // logcat. On the other hand, if there really are 4096 layers
                 // something has gone totally wrong its probably the most
                 // useful information in logcat.
                 if (rand() % 20 == 13) {
-                    ALOGE("Layer: %s", layer->getName().c_str());
+                    ALOGE("Layer: %s%s", layer->getName().c_str(),
+                          (layer->isHandleAlive() ? "handleAlive" : ""));
+                    std::this_thread::sleep_for(std::chrono::milliseconds(5));
                 }
             });
             ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
                   mOffscreenLayers.size());
             for (Layer* offscreenLayer : mOffscreenLayers) {
                 if (rand() % 20 == 13) {
-                    ALOGE("Offscreen-layer: %s", offscreenLayer->getName().c_str());
+                    ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(),
+                          (offscreenLayer->isHandleAlive() ? "handleAlive" : ""));
+                    std::this_thread::sleep_for(std::chrono::milliseconds(5));
                 }
             }
         }));
@@ -4405,7 +4420,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);
     }
@@ -4484,24 +4499,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)) {
@@ -4517,10 +4535,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) {
@@ -4567,12 +4581,11 @@
                            displays,
                            flags,
                            applyToken,
-                           inputWindowCommands,
+                           std::move(inputWindowCommands),
                            desiredPresentTime,
                            isAutoTimestamp,
                            std::move(uncacheBufferIds),
                            postTime,
-                           permissions,
                            hasListenerCallbacks,
                            listenerCallbacks,
                            originPid,
@@ -4601,14 +4614,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);
         }
     }
@@ -4625,12 +4636,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)) {
@@ -4647,12 +4658,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);
@@ -4689,7 +4695,6 @@
     uint32_t transactionFlags = 0;
     for (auto& transaction : transactions) {
         for (DisplayState& display : transaction.displays) {
-            display.sanitize(transaction.permissions);
             transactionFlags |= setDisplayStateLocked(display);
         }
     }
@@ -4788,10 +4793,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) {
@@ -5140,10 +5143,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) {
@@ -5425,7 +5426,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++;
@@ -5501,14 +5501,19 @@
             onActiveDisplayChangedLocked(activeDisplay.get(), *display);
         }
 
-        // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
-        // We can merge the syscall later.
-        if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
-            ALOGW("Couldn't set uclamp.min on display on: %s\n", strerror(errno));
+        if (displayId == mActiveDisplayId) {
+            // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
+            // set it before SCHED_FIFO due to b/190237315.
+            if (setSchedAttr(true) != NO_ERROR) {
+                ALOGW("Failed to set uclamp.min after powering on active display: %s",
+                      strerror(errno));
+            }
+            if (setSchedFifo(true) != NO_ERROR) {
+                ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
+                      strerror(errno));
+            }
         }
-        if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
-            ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
-        }
+
         getHwComposer().setPowerMode(displayId, mode);
         if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
             setHWCVsyncEnabled(displayId,
@@ -5522,15 +5527,21 @@
         scheduleComposite(FrameHint::kActive);
     } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
-        if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
-            ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
-        }
-        if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
-            ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
-        }
-        if (displayId == mActiveDisplayId && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
-            mScheduler->disableHardwareVsync(displayId, true);
-            mScheduler->enableSyntheticVsync();
+
+        if (displayId == mActiveDisplayId) {
+            if (setSchedFifo(false) != NO_ERROR) {
+                ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+                      strerror(errno));
+            }
+            if (setSchedAttr(false) != NO_ERROR) {
+                ALOGW("Failed set uclamp.min after powering off active display: %s",
+                      strerror(errno));
+            }
+
+            if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+                mScheduler->disableHardwareVsync(displayId, true);
+                mScheduler->enableSyntheticVsync();
+            }
         }
 
         // Make sure HWVsync is disabled before turning off the display
@@ -7970,6 +7981,7 @@
 
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay.getTransformHint();
+    sActiveDisplayRotationFlags = ui::Transform::toRotationFlags(activeDisplay.getOrientation());
 
     // The policy of the new active/pacesetter display may have changed while it was inactive. In
     // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8cc0184..1182b84 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -331,6 +331,14 @@
     const DisplayDevice* getDisplayFromLayerStack(ui::LayerStack)
             REQUIRES(mStateLock, kMainThreadContext);
 
+    // TODO (b/259407931): Remove.
+    // TODO (b/281857977): This should be annotated with REQUIRES(kMainThreadContext), but this
+    // would require thread safety annotations throughout the frontend (in particular Layer and
+    // LayerFE).
+    static ui::Transform::RotationFlags getActiveDisplayRotationFlags() {
+        return sActiveDisplayRotationFlags;
+    }
+
 protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -510,7 +518,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,
@@ -731,14 +739,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);
@@ -759,12 +769,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.
@@ -1388,6 +1397,10 @@
 
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
+    // Must only be accessed on the main thread.
+    // TODO (b/259407931): Remove.
+    static ui::Transform::RotationFlags sActiveDisplayRotationFlags;
+
     bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
         return hasDisplay(
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
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/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 0495678..cf23169 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -91,7 +91,7 @@
     // Set uclamp.min setting on all threads, maybe an overkill but we want
     // to cover important threads like RenderEngine.
     if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
-        ALOGW("Couldn't set uclamp.min: %s\n", strerror(errno));
+        ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
     }
 
     // The binder threadpool we start will inherit sched policy and priority
@@ -155,7 +155,7 @@
     startDisplayService(); // dependency on SF getting registered above
 
     if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
-        ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
+        ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
     }
 
     // run surface flinger in this thread
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/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
new file mode 100644
index 0000000..7077523
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest {
+    static constexpr bool kWithMockScheduler = false;
+    ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {}
+
+    void SetUp() override {
+        injectMockScheduler(kInnerDisplayId);
+
+        // Inject inner and outer displays with uninitialized power modes.
+        constexpr bool kInitPowerMode = false;
+        {
+            InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+            mInnerDisplay = injector.inject();
+        }
+        {
+            OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            mOuterDisplay = injector.inject();
+        }
+
+        mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+        mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+        // The flags are a static variable, so by modifying them in the test, we
+        // are modifying the real ones used by SurfaceFlinger. Save the original
+        // flags so we can restore them on teardown. This isn't perfect - the
+        // phone may have been rotated during the test, so we're restoring the
+        // wrong flags. But if the phone is rotated, this may also fail the test.
+        mOldRotationFlags = mFlinger.mutableActiveDisplayRotationFlags();
+
+        // Reset to the expected default state.
+        mFlinger.mutableActiveDisplayRotationFlags() = ui::Transform::ROT_0;
+    }
+
+    void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; }
+
+    static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
+    static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+
+    sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
+    ui::Transform::RotationFlags mOldRotationFlags;
+};
+
+TEST_F(ActiveDisplayRotationFlagsTest, defaultRotation) {
+    ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_90;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+    auto displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_90;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+    mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, onActiveDisplayChanged) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+
+    mFlinger.onActiveDisplayChanged(mInnerDisplay.get(), *mOuterDisplay);
+    ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 55705ca..70f8a83 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,6 +70,7 @@
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
+        "ActiveDisplayRotationFlagsTest.cpp",
         "CompositionTest.cpp",
         "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a189c00..8e18a3f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -600,6 +600,10 @@
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
     auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
 
+    auto& mutableActiveDisplayRotationFlags() {
+        return SurfaceFlinger::sActiveDisplayRotationFlags;
+    }
+
     auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
 
     ~TestableSurfaceFlinger() {
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