Loading include/input/InputWindow.h +6 −0 Original line number Diff line number Diff line Loading @@ -119,7 +119,11 @@ struct InputWindowInfo { /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ // This value should NOT be used to uniquely identify the window. There may be different // input windows that have the same token. sp<IBinder> token; // This uniquely identifies the input window. int32_t id = 0; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; Loading Loading @@ -203,6 +207,8 @@ public: sp<IBinder> getToken() const; int32_t getId() const { return mInfo.id; } sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; } Loading libs/input/InputWindow.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ status_t InputWindowInfo::write(Parcel& output) const { status_t s = output.writeStrongBinder(token); if (s != OK) return s; output.writeInt32(id); output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); Loading Loading @@ -116,6 +117,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { } ret.token = token; ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); Loading libs/input/tests/InputWindow_test.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { sp<IBinder> touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); i.id = 1; i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; Loading Loading @@ -72,6 +73,7 @@ TEST(InputWindowInfo, Parcelling) { p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); Loading services/inputflinger/dispatcher/InputDispatcher.cpp +17 −5 Original line number Diff line number Diff line Loading @@ -240,6 +240,18 @@ static bool removeByValue(std::unordered_map<K, V>& map, const V& value) { return removed; } static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) { if (first == second) { return true; } if (first == nullptr || second == nullptr) { return false; } return first->getToken() == second->getToken(); } // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { Loading Loading @@ -3278,9 +3290,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId); std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens; std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById; for (const sp<InputWindowHandle>& handle : oldHandles) { oldHandlesByTokens[handle->getToken()] = handle; oldHandlesById[handle->getId()] = handle; } std::vector<sp<InputWindowHandle>> newHandles; Loading Loading @@ -3311,8 +3323,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken()); if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { Loading Loading @@ -3370,7 +3382,7 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& sp<InputWindowHandle> oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); if (oldFocusedWindowHandle != newFocusedWindowHandle) { if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, Loading services/inputflinger/tests/InputDispatcher_test.cpp +149 −0 Original line number Diff line number Diff line Loading @@ -526,6 +526,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; mInfo.id = 0; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; Loading Loading @@ -564,6 +565,13 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } void setId(int32_t id) { mInfo.id = id; } void setWindowScale(float xScale, float yScale) { mInfo.windowXScale = xScale; mInfo.windowYScale = yScale; } void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); Loading @@ -586,12 +594,21 @@ public: expectedFlags); } InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; } return mInputReceiver->consume(); } void assertNoEvents() { ASSERT_NE(mInputReceiver, nullptr) << "Call 'assertNoEvents' on a window with an InputReceiver"; mInputReceiver->assertNoEvents(); } sp<IBinder> getToken() { return mInfo.token; } private: std::unique_ptr<FakeInputReceiver> mInputReceiver; }; Loading Loading @@ -667,6 +684,10 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, const std::vector<PointF>& points) { size_t pointerCount = points.size(); if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; } PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; Loading Loading @@ -1291,4 +1312,132 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownWasNotCalled(); } // These tests ensures we can send touch events to a single client when there are multiple input // windows that point to the same client token. class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); sp<FakeApplicationHandle> application = new FakeApplicationHandle(); mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", ADISPLAY_ID_DEFAULT); // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); mWindow1->setId(0); mWindow1->setFrame(Rect(0, 0, 100, 100)); mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", ADISPLAY_ID_DEFAULT, mWindow1->getToken()); mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); } protected: sp<FakeWindowHandle> mWindow1; sp<FakeWindowHandle> mWindow2; // Helper function to convert the point from screen coordinates into the window's space static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); return {x, y}; } void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, const std::vector<PointF>& points) { std::string name = window->mName; InputEvent* event = window->consume(); ASSERT_NE(nullptr, event) << name.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION) << " event, got " << inputEventTypeToString(event->getType()) << " event"; const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); EXPECT_EQ(expectedAction, motionEvent.getAction()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; float expectedY = points[i].y; EXPECT_EQ(expectedX, motionEvent.getX(i)) << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() << ", got " << motionEvent.getX(i); EXPECT_EQ(expectedY, motionEvent.getY(i)) << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() << ", got " << motionEvent.getY(i); } } }; TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); // Release touch on Window 1 motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // consume the UP event consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); // Release touch on Window 1 motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // consume the UP event consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } } // namespace android::inputdispatcher Loading
include/input/InputWindow.h +6 −0 Original line number Diff line number Diff line Loading @@ -119,7 +119,11 @@ struct InputWindowInfo { /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ // This value should NOT be used to uniquely identify the window. There may be different // input windows that have the same token. sp<IBinder> token; // This uniquely identifies the input window. int32_t id = 0; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; Loading Loading @@ -203,6 +207,8 @@ public: sp<IBinder> getToken() const; int32_t getId() const { return mInfo.id; } sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; } Loading
libs/input/InputWindow.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ status_t InputWindowInfo::write(Parcel& output) const { status_t s = output.writeStrongBinder(token); if (s != OK) return s; output.writeInt32(id); output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); Loading Loading @@ -116,6 +117,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { } ret.token = token; ret.id = from.readInt32(); ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); Loading
libs/input/tests/InputWindow_test.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ TEST(InputWindowInfo, Parcelling) { sp<IBinder> touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); i.id = 1; i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; Loading Loading @@ -72,6 +73,7 @@ TEST(InputWindowInfo, Parcelling) { p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +17 −5 Original line number Diff line number Diff line Loading @@ -240,6 +240,18 @@ static bool removeByValue(std::unordered_map<K, V>& map, const V& value) { return removed; } static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) { if (first == second) { return true; } if (first == nullptr || second == nullptr) { return false; } return first->getToken() == second->getToken(); } // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { Loading Loading @@ -3278,9 +3290,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( // Since we compare the pointer of input window handles across window updates, we need // to make sure the handle object for the same window stays unchanged across updates. const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId); std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens; std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById; for (const sp<InputWindowHandle>& handle : oldHandles) { oldHandlesByTokens[handle->getToken()] = handle; oldHandlesById[handle->getId()] = handle; } std::vector<sp<InputWindowHandle>> newHandles; Loading Loading @@ -3311,8 +3323,8 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( continue; } if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken()); if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) { const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { Loading Loading @@ -3370,7 +3382,7 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& sp<InputWindowHandle> oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); if (oldFocusedWindowHandle != newFocusedWindowHandle) { if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, Loading
services/inputflinger/tests/InputDispatcher_test.cpp +149 −0 Original line number Diff line number Diff line Loading @@ -526,6 +526,7 @@ public: mInfo.applicationInfo = *inputApplicationHandle->getInfo(); mInfo.token = token; mInfo.id = 0; mInfo.name = name; mInfo.layoutParamsFlags = 0; mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; Loading Loading @@ -564,6 +565,13 @@ public: void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } void setId(int32_t id) { mInfo.id = id; } void setWindowScale(float xScale, float yScale) { mInfo.windowXScale = xScale; mInfo.windowYScale = yScale; } void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); Loading @@ -586,12 +594,21 @@ public: expectedFlags); } InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; } return mInputReceiver->consume(); } void assertNoEvents() { ASSERT_NE(mInputReceiver, nullptr) << "Call 'assertNoEvents' on a window with an InputReceiver"; mInputReceiver->assertNoEvents(); } sp<IBinder> getToken() { return mInfo.token; } private: std::unique_ptr<FakeInputReceiver> mInputReceiver; }; Loading Loading @@ -667,6 +684,10 @@ static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLA static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId, const std::vector<PointF>& points) { size_t pointerCount = points.size(); if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; } PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; Loading Loading @@ -1291,4 +1312,132 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownWasNotCalled(); } // These tests ensures we can send touch events to a single client when there are multiple input // windows that point to the same client token. class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); sp<FakeApplicationHandle> application = new FakeApplicationHandle(); mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", ADISPLAY_ID_DEFAULT); // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); mWindow1->setId(0); mWindow1->setFrame(Rect(0, 0, 100, 100)); mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", ADISPLAY_ID_DEFAULT, mWindow1->getToken()); mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | InputWindowInfo::FLAG_SPLIT_TOUCH); mWindow2->setId(1); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT); } protected: sp<FakeWindowHandle> mWindow1; sp<FakeWindowHandle> mWindow2; // Helper function to convert the point from screen coordinates into the window's space static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); return {x, y}; } void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, const std::vector<PointF>& points) { std::string name = window->mName; InputEvent* event = window->consume(); ASSERT_NE(nullptr, event) << name.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION) << " event, got " << inputEventTypeToString(event->getType()) << " event"; const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); EXPECT_EQ(expectedAction, motionEvent.getAction()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; float expectedY = points[i].y; EXPECT_EQ(expectedX, motionEvent.getX(i)) << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str() << ", got " << motionEvent.getX(i); EXPECT_EQ(expectedY, motionEvent.getY(i)) << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str() << ", got " << motionEvent.getY(i); } } }; TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); // Release touch on Window 1 motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // consume the UP event consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); // Release touch on Window 1 motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // consume the UP event consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchedPoint}); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } } // namespace android::inputdispatcher