Merge "Add display Power Mode information while layer dumps" into sc-v2-dev
diff --git a/include/input/Input.h b/include/input/Input.h
index cd110e8..4adaa5b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1003,6 +1003,25 @@
     std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
 };
 
+/*
+ * Describes a unique request to enable or disable Pointer Capture.
+ */
+struct PointerCaptureRequest {
+public:
+    inline PointerCaptureRequest() : enable(false), seq(0) {}
+    inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+    inline bool operator==(const PointerCaptureRequest& other) const {
+        return enable == other.enable && seq == other.seq;
+    }
+    explicit inline operator bool() const { return enable; }
+
+    // True iff this is a request to enable Pointer Capture.
+    bool enable;
+
+    // The sequence number for the request.
+    uint32_t seq;
+};
+
 } // namespace android
 
 #endif // _LIBINPUT_INPUT_H
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 445df9e..c415ea0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1398,6 +1398,25 @@
     return ret;
 }
 
+#ifndef __ANDROID_VNDK__
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+                                              uint32_t *async_received)
+{
+    int ret = 0;
+    binder_frozen_status_info info;
+    info.pid = pid;
+
+#if defined(__ANDROID__)
+    if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+        ret = -errno;
+#endif
+    *sync_received = info.sync_recv;
+    *async_received = info.async_recv;
+
+    return ret;
+}
+#endif
+
 status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
     struct binder_freeze_info info;
     int ret = 0;
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 9dea3b4..793795e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -74,6 +74,8 @@
     //
     // Indicates whether the process has received any sync calls since last
     // freeze (cleared at freeze/unfreeze)
+    // bit 0: received sync transaction after being frozen
+    // bit 1: new pending sync transaction during freezing
     //
     __u32 sync_recv;
     //
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 196a41b..b401ea9 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -53,6 +53,13 @@
     // Provide information about the state of a frozen process
     static  status_t            getProcessFreezeInfo(pid_t pid, bool *sync_received,
                                                     bool *async_received);
+
+    // TODO: Remove the above legacy duplicated function in next version
+#ifndef __ANDROID_VNDK__
+    static  status_t            getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
+                                                    uint32_t *async_received);
+#endif
+
             sp<ProcessState>    process();
             
             status_t            clearLastError();
diff --git a/libs/binder/libbinder.arm32.map b/libs/binder/libbinder.arm32.map
index f26c33d..9f10b67 100644
--- a/libs/binder/libbinder.arm32.map
+++ b/libs/binder/libbinder.arm32.map
@@ -149,6 +149,7 @@
     _ZN7android14IPCThreadState20clearCallingIdentityEv;
     _ZN7android14IPCThreadState20getAndExecuteCommandEv;
     _ZN7android14IPCThreadState20getProcessFreezeInfoEiPbS1_;
+    _ZN7android14IPCThreadState20getProcessFreezeInfoEiPjS1_;
     _ZN7android14IPCThreadState20handlePolledCommandsEv;
     _ZN7android14IPCThreadState20processPendingDerefsEv;
     _ZN7android14IPCThreadState20writeTransactionDataEijijRKNS_6ParcelEPi;
diff --git a/libs/binder/libbinder.arm64.map b/libs/binder/libbinder.arm64.map
index 6211250..1d3da4c 100644
--- a/libs/binder/libbinder.arm64.map
+++ b/libs/binder/libbinder.arm64.map
@@ -150,6 +150,7 @@
     _ZN7android14IPCThreadState20clearCallingIdentityEv;
     _ZN7android14IPCThreadState20getAndExecuteCommandEv;
     _ZN7android14IPCThreadState20getProcessFreezeInfoEiPbS1_;
+    _ZN7android14IPCThreadState20getProcessFreezeInfoEiPjS1_;
     _ZN7android14IPCThreadState20handlePolledCommandsEv;
     _ZN7android14IPCThreadState20processPendingDerefsEv;
     _ZN7android14IPCThreadState20writeTransactionDataEijijRKNS_6ParcelEPi;
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5612d1d..64196ba 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -425,31 +425,30 @@
 
 TEST_F(BinderLibTest, Freeze) {
     Parcel data, reply, replypid;
-    std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+    std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
 
-    //Pass test on devices where the freezer is not supported
+    // Pass test on devices where the cgroup v2 freezer is not supported
     if (freezer_file.fail()) {
         GTEST_SKIP();
         return;
     }
 
-    std::string freezer_enabled;
-    std::getline(freezer_file, freezer_enabled);
-
-    //Pass test on devices where the freezer is disabled
-    if (freezer_enabled != "1") {
-        GTEST_SKIP();
-        return;
-    }
-
     EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
     int32_t pid = replypid.readInt32();
     for (int i = 0; i < 10; i++) {
         EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
     }
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
-    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
-    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+
+    // Pass test on devices where BINDER_FREEZE ioctl is not supported
+    int ret = IPCThreadState::self()->freeze(pid, false, 0);
+    if (ret != 0) {
+        GTEST_SKIP();
+        return;
+    }
+
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+    EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
     EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 
     bool sync_received, async_received;
@@ -460,6 +459,14 @@
     EXPECT_EQ(sync_received, 1);
     EXPECT_EQ(async_received, 0);
 
+    uint32_t sync_received2, async_received2;
+
+    EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
+                &async_received2));
+
+    EXPECT_EQ(sync_received2, 1);
+    EXPECT_EQ(async_received2, 0);
+
     EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
     EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
 }
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 33b3e1e..71b0f5f 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -287,16 +287,16 @@
 
 // --- NotifyPointerCaptureChangedArgs ---
 
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime,
-                                                                 bool enabled)
-      : NotifyArgs(id, eventTime), enabled(enabled) {}
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+      : NotifyArgs(id, eventTime), request(request) {}
 
 NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
         const NotifyPointerCaptureChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {}
+      : NotifyArgs(other.id, other.eventTime), request(other.request) {}
 
 bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled;
+    return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
 }
 
 void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index f26a9a9..05ef489 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -67,6 +67,9 @@
     if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
         result += "EXTERNAL_STYLUS_PRESENCE | ";
     }
+    if (changes & CHANGE_POINTER_CAPTURE) {
+        result += "POINTER_CAPTURE | ";
+    }
     if (changes & CHANGE_ENABLED_STATE) {
         result += "ENABLED_STATE | ";
     }
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5e9facf..6ce0313 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -116,7 +116,7 @@
 
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
 
-    void setPointerCapture(bool enabled) override {}
+    void setPointerCapture(const PointerCaptureRequest&) override {}
 
     void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index b5ed064..82b4fe4 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -119,15 +119,15 @@
 // PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
 // for all entries.
 PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
-                                                       bool hasPointerCapture)
+                                                       const PointerCaptureRequest& request)
       : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
-        pointerCaptureEnabled(hasPointerCapture) {}
+        pointerCaptureRequest(request) {}
 
 PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
 
 std::string PointerCaptureChangedEntry::getDescription() const {
     return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
-                        pointerCaptureEnabled ? "true" : "false");
+                        pointerCaptureRequest.enable ? "true" : "false");
 }
 
 // --- DragEntry ---
@@ -326,8 +326,7 @@
         keyEntry(nullptr),
         userActivityEventType(0),
         seq(0),
-        handled(false),
-        enabled(false) {}
+        handled(false) {}
 
 CommandEntry::~CommandEntry() {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 1b7fcf2..ffe3bb6 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -104,9 +104,9 @@
 };
 
 struct PointerCaptureChangedEntry : EventEntry {
-    bool pointerCaptureEnabled;
+    const PointerCaptureRequest pointerCaptureRequest;
 
-    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
     std::string getDescription() const override;
 
     ~PointerCaptureChangedEntry() override;
@@ -286,7 +286,7 @@
     sp<IBinder> oldToken;
     sp<IBinder> newToken;
     std::string obscuringPackage;
-    bool enabled;
+    PointerCaptureRequest pointerCaptureRequest;
     int32_t pid;
     nsecs_t consumeTime; // time when the event was consumed by InputConsumer
     int32_t displayId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c60a62f..65fec7d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -532,7 +532,6 @@
         mInTouchMode(true),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
-        mFocusedWindowRequestedPointerCapture(false),
         mWindowTokenWithPointerCapture(nullptr),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator),
@@ -1319,36 +1318,51 @@
 void InputDispatcher::dispatchPointerCaptureChangedLocked(
         nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
         DropReason& dropReason) {
+    dropReason = DropReason::NOT_DROPPED;
+
     const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
-    if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
-        LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
-    }
-    if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
-        // Pointer capture was already forcefully disabled because of focus change.
-        dropReason = DropReason::NOT_DROPPED;
-        return;
-    }
-
-    // Set drop reason for early returns
-    dropReason = DropReason::NO_POINTER_CAPTURE;
-
     sp<IBinder> token;
-    if (entry->pointerCaptureEnabled) {
-        // Enable Pointer Capture
-        if (!mFocusedWindowRequestedPointerCapture) {
+
+    if (entry->pointerCaptureRequest.enable) {
+        // Enable Pointer Capture.
+        if (haveWindowWithPointerCapture &&
+            (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
+            LOG_ALWAYS_FATAL("This request to enable Pointer Capture has already been dispatched "
+                             "to the window.");
+        }
+        if (!mCurrentPointerCaptureRequest.enable) {
             // This can happen if a window requests capture and immediately releases capture.
             ALOGW("No window requested Pointer Capture.");
+            dropReason = DropReason::NO_POINTER_CAPTURE;
             return;
         }
+        if (entry->pointerCaptureRequest.seq != mCurrentPointerCaptureRequest.seq) {
+            ALOGI("Skipping dispatch of Pointer Capture being enabled: sequence number mismatch.");
+            return;
+        }
+
         token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
         LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
         mWindowTokenWithPointerCapture = token;
     } else {
-        // Disable Pointer Capture
+        // Disable Pointer Capture.
+        // We do not check if the sequence number matches for requests to disable Pointer Capture
+        // for two reasons:
+        //  1. Pointer Capture can be disabled by a focus change, which means we can get two entries
+        //     to disable capture with the same sequence number: one generated by
+        //     disablePointerCaptureForcedLocked() and another as an acknowledgement of Pointer
+        //     Capture being disabled in InputReader.
+        //  2. We respect any request to disable Pointer Capture generated by InputReader, since the
+        //     actual Pointer Capture state that affects events being generated by input devices is
+        //     in InputReader.
+        if (!haveWindowWithPointerCapture) {
+            // Pointer capture was already forcefully disabled because of focus change.
+            dropReason = DropReason::NOT_DROPPED;
+            return;
+        }
         token = mWindowTokenWithPointerCapture;
         mWindowTokenWithPointerCapture = nullptr;
-        if (mFocusedWindowRequestedPointerCapture) {
-            mFocusedWindowRequestedPointerCapture = false;
+        if (mCurrentPointerCaptureRequest.enable) {
             setPointerCaptureLocked(false);
         }
     }
@@ -1357,8 +1371,7 @@
     if (channel == nullptr) {
         // Window has gone away, clean up Pointer Capture state.
         mWindowTokenWithPointerCapture = nullptr;
-        if (mFocusedWindowRequestedPointerCapture) {
-            mFocusedWindowRequestedPointerCapture = false;
+        if (mCurrentPointerCaptureRequest.enable) {
             setPointerCaptureLocked(false);
         }
         return;
@@ -3193,7 +3206,7 @@
                         static_cast<const PointerCaptureChangedEntry&>(eventEntry);
                 status = connection->inputPublisher
                                  .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
-                                                      captureEntry.pointerCaptureEnabled);
+                                                      captureEntry.pointerCaptureRequest.enable);
                 break;
             }
 
@@ -3991,14 +4004,14 @@
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
-          args->enabled ? "true" : "false");
+          args->request.enable ? "true" : "false");
 #endif
 
     bool needWake;
     { // acquire lock
         std::scoped_lock _l(mLock);
         auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
-                                                                  args->enabled);
+                                                                  args->request);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
 
@@ -4784,6 +4797,18 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
+std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
+        const sp<IBinder>& token) {
+    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+        for (TouchedWindow& w : state.windows) {
+            if (w.windowHandle->getToken() == token) {
+                return std::make_pair(&state, &w);
+            }
+        }
+    }
+    return std::make_pair(nullptr, nullptr);
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                          bool isDragDrop) {
     if (fromToken == toToken) {
@@ -4796,58 +4821,43 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
-        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
-            ALOGW("Cannot transfer focus because from or to window not found.");
+        // Find the target touch state and touched window by fromToken.
+        auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+        if (state == nullptr || touchedWindow == nullptr) {
+            ALOGD("Focus transfer failed because from window is not being touched.");
             return false;
         }
+
+        const int32_t displayId = state->displayId;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Cannot transfer focus because to window not found.");
+            return false;
+        }
+
         if (DEBUG_FOCUS) {
             ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-        }
-        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Cannot transfer focus because windows are on different displays.");
-            }
-            return false;
+                  touchedWindow->windowHandle->getName().c_str(),
+                  toWindowHandle->getName().c_str());
         }
 
-        bool found = false;
-        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
-            TouchState& state = pair.second;
-            for (size_t i = 0; i < state.windows.size(); i++) {
-                const TouchedWindow& touchedWindow = state.windows[i];
-                if (touchedWindow.windowHandle == fromWindowHandle) {
-                    int32_t oldTargetFlags = touchedWindow.targetFlags;
-                    BitSet32 pointerIds = touchedWindow.pointerIds;
+        // Erase old window.
+        int32_t oldTargetFlags = touchedWindow->targetFlags;
+        BitSet32 pointerIds = touchedWindow->pointerIds;
+        state->removeWindowByToken(fromToken);
 
-                    state.windows.erase(state.windows.begin() + i);
+        // Add new window.
+        int32_t newTargetFlags = oldTargetFlags &
+                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+                 InputTarget::FLAG_DISPATCH_AS_IS);
+        state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
-                    int32_t newTargetFlags = oldTargetFlags &
-                            (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
-                             InputTarget::FLAG_DISPATCH_AS_IS);
-                    state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
-                    // Store the dragging window.
-                    if (isDragDrop) {
-                        mDragState = std::make_unique<DragState>(toWindowHandle);
-                    }
-
-                    found = true;
-                    goto Found;
-                }
-            }
-        }
-    Found:
-
-        if (!found) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus transfer failed because from window did not have focus.");
-            }
-            return false;
+        // Store the dragging window.
+        if (isDragDrop) {
+            mDragState = std::make_unique<DragState>(toWindowHandle);
         }
 
+        // Synthesize cancel for old window and down for new window.
         sp<Connection> fromConnection = getConnectionLocked(fromToken);
         sp<Connection> toConnection = getConnectionLocked(toToken);
         if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4875,27 +4885,20 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
+                               [](const auto& pair) { return pair.second.windows.size() == 1; });
+        if (it == mTouchStatesByDisplay.end()) {
+            ALOGW("Cannot transfer touch state because there is no exact window being touched");
+            return false;
+        }
+        const int32_t displayId = it->first;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
         if (toWindowHandle == nullptr) {
             ALOGW("Could not find window associated with token=%p", destChannelToken.get());
             return false;
         }
 
-        const int32_t displayId = toWindowHandle->getInfo()->displayId;
-
-        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-        if (touchStateIt == mTouchStatesByDisplay.end()) {
-            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
-                  displayId);
-            return false;
-        }
-
-        TouchState& state = touchStateIt->second;
-        if (state.windows.size() != 1) {
-            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
-                  state.windows.size());
-            return false;
-        }
+        TouchState& state = it->second;
         const TouchedWindow& touchedWindow = state.windows[0];
         fromToken = touchedWindow.windowHandle->getToken();
     } // release lock
@@ -4937,8 +4940,8 @@
 std::string InputDispatcher::dumpPointerCaptureStateLocked() {
     std::string dump;
 
-    dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
-                         toString(mFocusedWindowRequestedPointerCapture));
+    dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
+                         toString(mCurrentPointerCaptureRequest.enable));
 
     std::string windowName = "None";
     if (mWindowTokenWithPointerCapture) {
@@ -4947,7 +4950,7 @@
         windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
                                          : "token has capture without window";
     }
-    dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+    dump += StringPrintf(INDENT "Current Window with Pointer Capture: %s\n", windowName.c_str());
 
     return dump;
 }
@@ -5416,14 +5419,13 @@
             return;
         }
 
-        if (enabled == mFocusedWindowRequestedPointerCapture) {
+        if (enabled == mCurrentPointerCaptureRequest.enable) {
             ALOGW("Ignoring request to %s Pointer Capture: "
                   "window has %s requested pointer capture.",
                   enabled ? "enable" : "disable", enabled ? "already" : "not");
             return;
         }
 
-        mFocusedWindowRequestedPointerCapture = enabled;
         setPointerCaptureLocked(enabled);
     } // release lock
 
@@ -6182,14 +6184,13 @@
 }
 
 void InputDispatcher::disablePointerCaptureForcedLocked() {
-    if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+    if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
         return;
     }
 
     ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
 
-    if (mFocusedWindowRequestedPointerCapture) {
-        mFocusedWindowRequestedPointerCapture = false;
+    if (mCurrentPointerCaptureRequest.enable) {
         setPointerCaptureLocked(false);
     }
 
@@ -6206,14 +6207,16 @@
     }
 
     auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
-                                                              false /* hasCapture */);
+                                                              mCurrentPointerCaptureRequest);
     mInboundQueue.push_front(std::move(entry));
 }
 
 void InputDispatcher::setPointerCaptureLocked(bool enabled) {
+    mCurrentPointerCaptureRequest.enable = enabled;
+    mCurrentPointerCaptureRequest.seq++;
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doSetPointerCaptureLockedInterruptible);
-    commandEntry->enabled = enabled;
+    commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest;
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -6221,7 +6224,7 @@
         android::inputdispatcher::CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->setPointerCapture(commandEntry->enabled);
+    mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest);
 
     mLock.lock();
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2436e73..db559df 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -365,10 +365,12 @@
 
     // Keeps track of the focused window per display and determines focus changes.
     FocusResolver mFocusResolver GUARDED_BY(mLock);
-    // Whether the focused window on the focused display has requested Pointer Capture.
-    // The state of this variable should always be in sync with the state of Pointer Capture in the
-    // policy, which is updated through setPointerCaptureLocked(enabled).
-    bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+    // The enabled state of this request is true iff the focused window on the focused display has
+    // requested Pointer Capture. This request also contains the sequence number associated with the
+    // current request. The state of this variable should always be in sync with the state of
+    // Pointer Capture in the policy, and is only updated through setPointerCaptureLocked(request).
+    PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
 
     // The window token that has Pointer Capture.
     // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
@@ -378,7 +380,7 @@
     void disablePointerCaptureForcedLocked() REQUIRES(mLock);
 
     // Set the Pointer Capture state in the Policy.
-    void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+    void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
 
     // Dispatcher state at time of last ANR.
     std::string mLastAnrState GUARDED_BY(mLock);
@@ -653,6 +655,10 @@
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
+    // Find touched state and touched window by token.
+    std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
+            REQUIRES(mLock);
+
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
     LatencyTracker mLatencyTracker GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index ebfcbe1..3c1e637 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -156,7 +156,7 @@
      *
      * InputDispatcher is solely responsible for updating the Pointer Capture state.
      */
-    virtual void setPointerCapture(bool enabled) = 0;
+    virtual void setPointerCapture(const PointerCaptureRequest&) = 0;
 
     /* Notifies the policy that the drag window has moved over to another window */
     virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
index 8da699d..0b44187 100644
--- a/services/inputflinger/docs/pointer_capture.md
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -17,6 +17,8 @@
 
 `InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
 
+We use a sequence number to synchronize different requests to enable Pointer Capture between InputReader and InputDispatcher.
+
 ### Enabling Pointer Capture
 
 There are four key steps that take place when Pointer Capture is enabled:
@@ -40,5 +42,5 @@
 
 `InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
 
-- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mCurrentPointerCaptureRequest`: The sequence number of the current Pointer Capture request. This request is enabled iff the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
 - `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 4b7d26d..fe74214 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -211,11 +211,12 @@
 
 /* Describes a change in the state of Pointer Capture. */
 struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
-    bool enabled;
+    // The sequence number of the Pointer Capture request, if enabled.
+    PointerCaptureRequest request;
 
     inline NotifyPointerCaptureChangedArgs() {}
 
-    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled);
+    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
 
     NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 7fdbbfd..3c8ac1c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -279,29 +279,30 @@
     // True to show the location of touches on the touch screen as spots.
     bool showTouches;
 
-    // True if pointer capture is enabled.
-    bool pointerCapture;
+    // The latest request to enable or disable Pointer Capture.
+    PointerCaptureRequest pointerCaptureRequest;
 
     // The set of currently disabled input devices.
     std::set<int32_t> disabledDevices;
 
-    InputReaderConfiguration() :
-            virtualKeyQuietTime(0),
+    InputReaderConfiguration()
+          : virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGesturesEnabled(true),
-            pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
-            pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
-            pointerGestureTapInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
-            pointerGestureTapSlop(10.0f), // 10 pixels
+            pointerGestureQuietInterval(100 * 1000000LL),            // 100 ms
+            pointerGestureDragMinSwitchSpeed(50),                    // 50 pixels per second
+            pointerGestureTapInterval(150 * 1000000LL),              // 150 ms
+            pointerGestureTapDragInterval(150 * 1000000LL),          // 150 ms
+            pointerGestureTapSlop(10.0f),                            // 10 pixels
             pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
-            pointerGestureMultitouchMinDistance(15), // 15 pixels
-            pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+            pointerGestureMultitouchMinDistance(15),                 // 15 pixels
+            pointerGestureSwipeTransitionAngleCosine(0.2588f),       // cosine of 75 degrees
             pointerGestureSwipeMaxWidthRatio(0.25f),
             pointerGestureMovementSpeedRatio(0.8f),
             pointerGestureZoomSpeedRatio(0.3f),
-            showTouches(false), pointerCapture(false) { }
+            showTouches(false),
+            pointerCaptureRequest() {}
 
     static std::string changesToString(uint32_t changes);
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 10c04f6..5120860 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -367,9 +367,15 @@
     }
 
     if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
-        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
-                                                   mConfig.pointerCapture);
-        mQueuedListener->notifyPointerCaptureChanged(&args);
+        if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
+            ALOGV("Skipping notifying pointer capture changes: "
+                  "There was no change in the pointer capture state.");
+        } else {
+            mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
+            const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+                                                       mCurrentPointerCaptureRequest);
+            mQueuedListener->notifyPointerCaptureChanged(&args);
+        }
     }
 }
 
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index a00c5af..e44aa0f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -230,6 +230,8 @@
     uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
     void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
 
+    PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
+
     // state queries
     typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 437902a..2ac41b1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -154,9 +154,9 @@
         mHWheelScale = 1.0f;
     }
 
-    if ((!changes && config->pointerCapture) ||
+    if ((!changes && config->pointerCaptureRequest.enable) ||
         (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) {
-        if (config->pointerCapture) {
+        if (config->pointerCaptureRequest.enable) {
             if (mParameters.mode == Parameters::MODE_POINTER) {
                 mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
                 mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f6fa7a1..29a0fc7 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -621,7 +621,7 @@
 
     // Determine device mode.
     if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
-        mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
+        mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
         mSource = AINPUT_SOURCE_MOUSE;
         mDeviceMode = DeviceMode::POINTER;
         if (hasStylus()) {
@@ -804,11 +804,12 @@
     // preserve the cursor position.
     if (mDeviceMode == DeviceMode::POINTER ||
         (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
-        (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
+        (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+         mConfig.pointerCaptureRequest.enable)) {
         if (mPointerController == nullptr) {
             mPointerController = getContext()->getPointerController(getDeviceId());
         }
-        if (mConfig.pointerCapture) {
+        if (mConfig.pointerCaptureRequest.enable) {
             mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
         }
     } else {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f884a4b..f028349 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -46,7 +46,8 @@
 static const int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
-static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t SECOND_DISPLAY_ID = 1;
 
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
@@ -242,19 +243,22 @@
         mConfig.keyRepeatDelay = delay;
     }
 
-    void waitForSetPointerCapture(bool enabled) {
+    PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
         std::unique_lock lock(mLock);
         base::ScopedLockAssertion assumeLocked(mLock);
 
         if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
                                                       [this, enabled]() REQUIRES(mLock) {
-                                                          return mPointerCaptureEnabled &&
-                                                                  *mPointerCaptureEnabled ==
+                                                          return mPointerCaptureRequest->enable ==
                                                                   enabled;
                                                       })) {
-            FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+            ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled
+                          << ") to be called.";
+            return {};
         }
-        mPointerCaptureEnabled.reset();
+        auto request = *mPointerCaptureRequest;
+        mPointerCaptureRequest.reset();
+        return request;
     }
 
     void assertSetPointerCaptureNotCalled() {
@@ -262,11 +266,11 @@
         base::ScopedLockAssertion assumeLocked(mLock);
 
         if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
-            FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+            FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
                       "enabled = "
-                   << *mPointerCaptureEnabled;
+                   << std::to_string(mPointerCaptureRequest->enable);
         }
-        mPointerCaptureEnabled.reset();
+        mPointerCaptureRequest.reset();
     }
 
     void assertDropTargetEquals(const sp<IBinder>& targetToken) {
@@ -284,7 +288,8 @@
     std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
     std::condition_variable mPointerCaptureChangedCondition;
-    std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
+
+    std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
 
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
@@ -401,9 +406,9 @@
         mOnPointerDownToken = newToken;
     }
 
-    void setPointerCapture(bool enabled) override {
+    void setPointerCapture(const PointerCaptureRequest& request) override {
         std::scoped_lock lock(mLock);
-        mPointerCaptureEnabled = {enabled};
+        mPointerCaptureRequest = {request};
         mPointerCaptureChangedCondition.notify_all();
     }
 
@@ -937,6 +942,15 @@
         mInfo.displayId = displayId;
     }
 
+    sp<FakeWindowHandle> clone(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+        sp<FakeWindowHandle> handle =
+                new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
+                                     displayId, mInfo.token);
+        return handle;
+    }
+
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
 
     void setVisible(bool visible) { mInfo.visible = visible; }
@@ -1380,8 +1394,9 @@
     return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
 }
 
-static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
-    return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
+        const PointerCaptureRequest& request) {
+    return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
@@ -1996,6 +2011,134 @@
     secondWindow->assertNoEvents();
 }
 
+// This case will create two windows and one mirrored window on the default display and mirror
+// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
+// the windows info of second display before default display.
+TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
+                                                secondWindowInPrimary->getToken()));
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel();
+    secondWindowInPrimary->consumeMotionDown();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp();
+}
+
+// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
+// 'transferTouch' api.
+TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    // Touch on second display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
+
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
+    secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2906,7 +3049,6 @@
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
@@ -3069,8 +3211,6 @@
 
 class InputFilterTest : public InputDispatcherTest {
 protected:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
     void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
         NotifyMotionArgs motionArgs;
 
@@ -4581,16 +4721,18 @@
         mWindow->consumeFocusEvent(true);
     }
 
-    void notifyPointerCaptureChanged(bool enabled) {
-        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+    void notifyPointerCaptureChanged(const PointerCaptureRequest& request) {
+        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request);
         mDispatcher->notifyPointerCaptureChanged(&args);
     }
 
-    void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+    PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
+                                                         bool enabled) {
         mDispatcher->requestPointerCapture(window->getToken(), enabled);
-        mFakePolicy->waitForSetPointerCapture(enabled);
-        notifyPointerCaptureChanged(enabled);
+        auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+        notifyPointerCaptureChanged(request);
         window->consumeCaptureEvent(enabled);
+        return request;
     }
 };
 
@@ -4612,7 +4754,7 @@
 }
 
 TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
-    requestAndVerifyPointerCapture(mWindow, true);
+    auto request = requestAndVerifyPointerCapture(mWindow, true);
 
     setFocusedWindow(mSecondWindow);
 
@@ -4620,26 +4762,26 @@
     mWindow->consumeCaptureEvent(false);
     mWindow->consumeFocusEvent(false);
     mSecondWindow->consumeFocusEvent(true);
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
 
     // Ensure that additional state changes from InputReader are not sent to the window.
-    notifyPointerCaptureChanged(false);
-    notifyPointerCaptureChanged(true);
-    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged({});
+    notifyPointerCaptureChanged(request);
+    notifyPointerCaptureChanged({});
     mWindow->assertNoEvents();
     mSecondWindow->assertNoEvents();
     mFakePolicy->assertSetPointerCaptureNotCalled();
 }
 
 TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
-    requestAndVerifyPointerCapture(mWindow, true);
+    auto request = requestAndVerifyPointerCapture(mWindow, true);
 
     // InputReader unexpectedly disables and enables pointer capture.
-    notifyPointerCaptureChanged(false);
-    notifyPointerCaptureChanged(true);
+    notifyPointerCaptureChanged({});
+    notifyPointerCaptureChanged(request);
 
     // Ensure that Pointer Capture is disabled.
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
     mWindow->consumeCaptureEvent(false);
     mWindow->assertNoEvents();
 }
@@ -4649,24 +4791,43 @@
 
     // The first window loses focus.
     setFocusedWindow(mSecondWindow);
-    mFakePolicy->waitForSetPointerCapture(false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
     mWindow->consumeCaptureEvent(false);
 
     // Request Pointer Capture from the second window before the notification from InputReader
     // arrives.
     mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
-    mFakePolicy->waitForSetPointerCapture(true);
+    auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
 
     // InputReader notifies Pointer Capture was disabled (because of the focus change).
-    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged({});
 
     // InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
-    notifyPointerCaptureChanged(true);
+    notifyPointerCaptureChanged(request);
 
     mSecondWindow->consumeFocusEvent(true);
     mSecondWindow->consumeCaptureEvent(true);
 }
 
+TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
+    // App repeatedly enables and disables capture.
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+    mDispatcher->requestPointerCapture(mWindow->getToken(), false);
+    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+
+    // InputReader notifies that PointerCapture has been enabled for the first request. Since the
+    // first request is now stale, this should do nothing.
+    notifyPointerCaptureChanged(firstRequest);
+    mWindow->assertNoEvents();
+
+    // InputReader notifies that the second request was enabled.
+    notifyPointerCaptureChanged(secondRequest);
+    mWindow->consumeCaptureEvent(true);
+}
+
 class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
 protected:
     constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 91da0d5..b419d9a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -312,8 +312,9 @@
         transform = t;
     }
 
-    void setPointerCapture(bool enabled) {
-        mConfig.pointerCapture = enabled;
+    PointerCaptureRequest setPointerCapture(bool enabled) {
+        mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+        return mConfig.pointerCaptureRequest;
     }
 
     void setShowTouches(bool enabled) {
@@ -327,6 +328,8 @@
     float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
 
 private:
+    uint32_t mNextPointerCaptureSequenceNumber = 0;
+
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
                                           int32_t orientation, bool isActive,
                                           const std::string& uniqueId,
@@ -1974,24 +1977,24 @@
 TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
     NotifyPointerCaptureChangedArgs args;
 
-    mFakePolicy->setPointerCapture(true);
+    auto request = mFakePolicy->setPointerCapture(true);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled.";
+    ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+    ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
 
     mFakePolicy->setPointerCapture(false);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+    ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
 
-    // Verify that the Pointer Capture state is re-configured correctly when the configuration value
+    // Verify that the Pointer Capture state is not updated when the configuration value
     // does not change.
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     mReader->loopOnce();
-    mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled.";
+    mFakeListener->assertNotifyCaptureWasNotCalled();
 }
 
 class FakeVibratorInputMapper : public FakeInputMapper {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fb7de97..6a26c63 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -100,6 +100,11 @@
                                                           "to have been called."));
 }
 
+void TestInputListener::assertNotifyCaptureWasNotCalled() {
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyPointerCaptureChangedArgs>(
+            "notifyPointerCaptureChanged() should not be called."));
+}
+
 template <class NotifyArgsType>
 void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
     std::unique_lock<std::mutex> lock(mLock);
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0ffcaaa..0a1dc4b 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -55,6 +55,7 @@
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
 
     void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+    void assertNotifyCaptureWasNotCalled();
     void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
     void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 217f3bb..aab8331 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -619,17 +619,17 @@
 }
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
-    const auto internalDisplayId = getInternalDisplayIdLocked();
-    if (!internalDisplayId) {
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) {
         return {};
     }
 
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(*internalDisplayId);
+    displayIds.push_back(display->getPhysicalId());
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
-        if (id != *internalDisplayId) {
+        if (id != display->getPhysicalId()) {
             displayIds.push_back(id);
         }
     }