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/include/input/InputWindow.h b/include/input/InputWindow.h
index 2b8cc57..a065a4c 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -163,6 +163,7 @@
     int32_t ownerUid;
     int32_t inputFeatures;
     int32_t displayId;
+    int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
 
     void addTouchableRegion(const Rect& region);
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index aa1371f..5c5613d 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -95,6 +95,7 @@
     output.writeInt32(ownerUid);
     output.writeInt32(inputFeatures);
     output.writeInt32(displayId);
+    output.writeInt32(portalToDisplayId);
     applicationInfo.write(output);
     output.write(touchableRegion);
 
@@ -136,6 +137,7 @@
     ret.ownerUid = from.readInt32();
     ret.inputFeatures = from.readInt32();
     ret.displayId = from.readInt32();
+    ret.portalToDisplayId = from.readInt32();
     ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
 
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 5e5893f..09dd72b 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -61,6 +61,7 @@
     i.ownerUid = 24;
     i.inputFeatures = 29;
     i.displayId = 34;
+    i.portalToDisplayId = 2;
 
     Parcel p;
     i.write(p);
@@ -90,6 +91,7 @@
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
+    ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
 }
 
 } // namespace test
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) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 327dbbd..2d8df5c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -921,7 +921,8 @@
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent;
 
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
+    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+            bool addOutsideTargets = false, bool addPortalWindows = false);
 
     // All registered connections mapped by channel file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByFd;
@@ -1016,12 +1017,18 @@
         int32_t displayId; // id to the display that currently has a touch, others are rejected
         Vector<TouchedWindow> windows;
 
+        // This collects the portal windows that the touch has gone through. Each portal window
+        // targets a display (embedded display for most cases). With this info, we can add the
+        // monitoring channels of the displays touched.
+        Vector<sp<InputWindowHandle>> portalWindows;
+
         TouchState();
         ~TouchState();
         void reset();
         void copyFrom(const TouchState& other);
         void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
                 int32_t targetFlags, BitSet32 pointerIds);
+        void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
         void removeWindow(const sp<InputWindowHandle>& windowHandle);
         void removeWindowByToken(const sp<IBinder>& token);
         void filterNonAsIsTouchWindows();
@@ -1096,7 +1103,8 @@
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
-    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId);
+    void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId,
+            float xOffset = 0, float yOffset = 0);
 
     void pokeUserActivityLocked(const EventEntry* eventEntry);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index aa9bc15..6faba52 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2138,6 +2138,10 @@
 InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
 
+    if (info.displayId == ADISPLAY_ID_NONE) {
+        info.displayId = mDrawingState.layerStack;
+    }
+
     ui::Transform t = getTransform();
     const float xScale = t.sx();
     const float yScale = t.sy();
@@ -2148,7 +2152,10 @@
     }
 
     // Transform layer size to screen space and inset it by surface insets.
-    Rect layerBounds = getBufferSize(getDrawingState());
+    // If this is a portal window, set the touchableRegion to the layerBounds.
+    Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+            ? getBufferSize(getDrawingState())
+            : info.touchableRegion.getBounds();
     if (!layerBounds.isValid()) {
         layerBounds = getCroppedBufferSize(getDrawingState());
     }