Introduce new portal window which transports touch to another display

It enables the user to directly touch on a window of a display
embedded on another one. The embedded displays can be nested.

The monitoring channels of the embedded display can also receive touch
events.

Bug: 120675821
Test: Manual test with ActivityViewTest
Test: atest -a inputflinger_tests
Test: atest CtsActivityManagerDeviceTestCases:ActivityViewTest
Change-Id: I773c7efb1b048080020aadd45156261a10095fcb
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9d92435..37c65f5 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -525,7 +525,7 @@
 }
 
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
-        int32_t x, int32_t y) {
+        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
     // Traverse windows from front to back to find touched window.
     const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     size_t numWindows = windowHandles.size();
@@ -540,10 +540,25 @@
                     bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                             | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
+                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
+                        if (portalToDisplayId != ADISPLAY_ID_NONE
+                                && portalToDisplayId != displayId) {
+                            if (addPortalWindows) {
+                                // For the monitoring channels of the display.
+                                mTempTouchState.addPortalWindow(windowHandle);
+                            }
+                            return findTouchedWindowAtLocked(
+                                    portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+                        }
                         // Found window.
                         return windowHandle;
                     }
                 }
+
+                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(
+                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+                }
             }
         }
     }
@@ -930,6 +945,22 @@
     // Add monitor channels from event's or focused display.
     addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
 
+    if (isPointerEvent) {
+        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
+        if (stateIndex >= 0) {
+            const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+            if (!state.portalWindows.isEmpty()) {
+                // The event has gone through these portal windows, so we add monitoring targets of
+                // the corresponding displays as well.
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const InputWindowInfo* windowInfo = state.portalWindows.itemAt(i)->getInfo();
+                    addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+                            -windowInfo->frameLeft, -windowInfo->frameTop);
+                }
+            }
+        }
+    }
+
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1297,37 +1328,8 @@
                 getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                 getAxisValue(AMOTION_EVENT_AXIS_Y));
-        sp<InputWindowHandle> newTouchedWindowHandle;
-        bool isTouchModal = false;
-
-        // Traverse windows from front to back to find touched window and outside targets.
-        const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
-        size_t numWindows = windowHandles.size();
-        for (size_t i = 0; i < numWindows; i++) {
-            sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
-            const InputWindowInfo* windowInfo = windowHandle->getInfo();
-            if (windowInfo->displayId != displayId) {
-                continue; // wrong display
-            }
-
-            int32_t flags = windowInfo->layoutParamsFlags;
-            if (windowInfo->visible) {
-                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
-                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                        newTouchedWindowHandle = windowHandle;
-                        break; // found touched window, exit window loop
-                    }
-                }
-
-                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(
-                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
-                }
-            }
-        }
+        sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+                displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
 
         // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr
@@ -1689,7 +1691,7 @@
 }
 
 void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
-        int32_t displayId) {
+        int32_t displayId, float xOffset, float yOffset) {
     std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
             mMonitoringChannelsByDisplay.find(displayId);
 
@@ -1702,8 +1704,8 @@
             InputTarget& target = inputTargets.editTop();
             target.inputChannel = monitoringChannels[i];
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-            target.xOffset = 0;
-            target.yOffset = 0;
+            target.xOffset = xOffset;
+            target.yOffset = yOffset;
             target.pointerIds.clear();
             target.globalScaleFactor = 1.0f;
         }
@@ -3107,7 +3109,8 @@
             Vector<sp<InputWindowHandle>> newHandles;
             for (size_t i = 0; i < numWindows; i++) {
                 const sp<InputWindowHandle>& handle = inputWindowHandles.itemAt(i);
-                if (!handle->updateInfo() || getInputChannelLocked(handle->getToken()) == nullptr) {
+                if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr
+                        && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) {
                     ALOGE("Window handle %s has no registered input channel",
                             handle->getName().c_str());
                     continue;
@@ -3542,6 +3545,14 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
+            if (!state.portalWindows.isEmpty()) {
+                dump += INDENT3 "Portal windows:\n";
+                for (size_t i = 0; i < state.portalWindows.size(); i++) {
+                    const sp<InputWindowHandle> portalWindowHandle = state.portalWindows.itemAt(i);
+                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
+                            i, portalWindowHandle->getName().c_str());
+                }
+            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
@@ -3558,11 +3569,12 @@
                     const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
-                            "paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                            "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, "
                             "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                             "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
                             "touchableRegion=",
                             i, windowInfo->name.c_str(), windowInfo->displayId,
+                            windowInfo->portalToDisplayId,
                             toString(windowInfo->paused),
                             toString(windowInfo->hasFocus),
                             toString(windowInfo->hasWallpaper),
@@ -4911,6 +4923,7 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
+    portalWindows.clear();
 }
 
 void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4920,6 +4933,7 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
+    portalWindows = other.portalWindows;
 }
 
 void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4948,6 +4962,17 @@
     touchedWindow.pointerIds = pointerIds;
 }
 
+void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+    size_t numWindows = portalWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        sp<InputWindowHandle> portalWindowHandle = portalWindows.itemAt(i);
+        if (portalWindowHandle == windowHandle) {
+            return;
+        }
+    }
+    portalWindows.push_back(windowHandle);
+}
+
 void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
     for (size_t i = 0; i < windows.size(); i++) {
         if (windows.itemAt(i).windowHandle == windowHandle) {