Loading services/inputflinger/dispatcher/InputDispatcher.cpp +106 −48 Original line number Diff line number Diff line Loading @@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, EventEntry* eventEntry, int32_t inputTargetFlags) { if (inputTarget.useDefaultPointerInfo()) { const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); return std::make_unique<DispatchEntry>(eventEntry, // increments ref inputTargetFlags, pointerInfo.xOffset, pointerInfo.yOffset, inputTarget.globalScaleFactor, pointerInfo.windowXScale, pointerInfo.windowYScale); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); PointerCoords pointerCoords[MAX_POINTERS]; // Use the first pointer information to normalize all other pointers. This could be any pointer // as long as all other pointers are normalized to the same value and the final DispatchEntry // uses the offset and scale for the normalized pointer. const PointerInfo& firstPointerInfo = inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; // Iterate through all pointers in the event to normalize against the first. for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; // The scale factor is the ratio of the current pointers scale to the normalized scale. float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); // First apply the current pointers offset to set the window at 0,0 pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); // Next scale the coordinates. pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); // Lastly, offset the coordinates so they're in the normalized pointer's frame. pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, -firstPointerInfo.yOffset); } MotionEntry* combinedMotionEntry = new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, motionEntry.action, motionEntry.actionButton, motionEntry.flags, motionEntry.metaState, motionEntry.buttonState, motionEntry.classification, motionEntry.edgeFlags, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, 0 /* yOffset */); return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref inputTargetFlags, firstPointerInfo.xOffset, firstPointerInfo.yOffset, inputTarget.globalScaleFactor, firstPointerInfo.windowXScale, firstPointerInfo.windowYScale); } // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { Loading Loading @@ -1111,7 +1172,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { if (DEBUG_FOCUS) { ALOGD("Dropping event delivery to target with channel '%s' because it " Loading Loading @@ -1785,23 +1846,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { return inputTarget.inputChannel->getConnectionToken() == windowHandle->getToken(); }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { InputTarget inputTarget; sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); if (inputChannel == nullptr) { ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); return; } inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } const InputWindowInfo* windowInfo = windowHandle->getInfo(); InputTarget target; target.inputChannel = inputChannel; target.flags = targetFlags; target.xOffset = -windowInfo->frameLeft; target.yOffset = -windowInfo->frameTop; target.globalScaleFactor = windowInfo->globalScaleFactor; target.windowXScale = windowInfo->windowXScale; target.windowYScale = windowInfo->windowYScale; target.pointerIds = pointerIds; inputTargets.push_back(target); ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, Loading @@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; target.xOffset = xOffset; target.yOffset = yOffset; target.pointerIds.clear(); target.globalScaleFactor = 1.0f; target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } Loading Loading @@ -2045,7 +2114,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", Loading @@ -2056,9 +2125,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " "windowScaleFactor=(%f, %f), pointerIds=0x%x", connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, inputTarget->windowYScale, inputTarget->pointerIds.value); connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, inputTarget.windowYScale, inputTarget.pointerIds.value); #endif // Skip this event if the connection status is not normal. Loading @@ -2072,13 +2141,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. if (inputTarget->flags & InputTarget::FLAG_SPLIT) { if (inputTarget.flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); if (!splitMotionEntry) { return; // split event was dropped } Loading @@ -2100,7 +2169,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 Loading Loading @@ -2133,7 +2202,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, const InputTarget& inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", Loading @@ -2141,7 +2210,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } int32_t inputTargetFlags = inputTarget->flags; int32_t inputTargetFlags = inputTarget.flags; if (!(inputTargetFlags & dispatchMode)) { return; } Loading @@ -2149,11 +2218,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, inputTarget->windowYScale); std::unique_ptr<DispatchEntry> dispatchEntry = createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); // Apply target flags and update the connection's input state. switch (eventEntry->type) { Loading @@ -2168,7 +2234,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName().c_str()); #endif delete dispatchEntry; return; // skip the inconsistent event } break; Loading Loading @@ -2215,12 +2280,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio "event", connection->getInputChannelName().c_str()); #endif delete dispatchEntry; return; // skip the inconsistent event } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget->inputChannel->getConnectionToken()); inputTarget.inputChannel->getConnectionToken()); break; } Loading @@ -2238,7 +2302,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } // Enqueue the dispatch entry. connection->outboundQueue.push_back(dispatchEntry); connection->outboundQueue.push_back(dispatchEntry.release()); traceOutboundQueueLength(connection); } Loading Loading @@ -2611,21 +2675,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; target.yOffset = -windowInfo->frameTop; target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; target.windowXScale = windowInfo->windowXScale; target.windowYScale = windowInfo->windowYScale; } else { target.xOffset = 0; target.yOffset = 0; target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref &target, InputTarget::FLAG_DISPATCH_AS_IS); target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } Loading services/inputflinger/dispatcher/InputDispatcher.h +3 −3 Original line number Diff line number Diff line Loading @@ -378,13 +378,13 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) const InputTarget& inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); Loading services/inputflinger/dispatcher/InputTarget.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,43 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, float windowXScale, float windowYScale) { // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors // and non splittable windows since we will just use all the pointers from the input event. if (newPointerIds.isEmpty()) { setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); return; } // Ensure that the new set of pointers doesn't overlap with the current set of pointers. ALOG_ASSERT((pointerIds & newPointerIds) == 0); pointerIds |= newPointerIds; while (!newPointerIds.isEmpty()) { int32_t pointerId = newPointerIds.clearFirstMarkedBit(); pointerInfos[pointerId].xOffset = xOffset; pointerInfos[pointerId].yOffset = yOffset; pointerInfos[pointerId].windowXScale = windowXScale; pointerInfos[pointerId].windowYScale = windowYScale; } } void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, float windowYScale) { pointerIds.clear(); pointerInfos[0].xOffset = xOffset; pointerInfos[0].yOffset = yOffset; pointerInfos[0].windowXScale = windowXScale; pointerInfos[0].windowYScale = windowYScale; } bool InputTarget::useDefaultPointerInfo() const { return pointerIds.isEmpty(); } const PointerInfo& InputTarget::getDefaultPointerInfo() const { return pointerInfos[0]; } } // namespace android::inputdispatcher services/inputflinger/dispatcher/InputTarget.h +39 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,22 @@ namespace android::inputdispatcher { /* * Information about each pointer for an InputTarget. This includes offset and scale so * all pointers can be normalized to a single offset and scale. */ struct PointerInfo { // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset = 0.0f; float yOffset = 0.0f; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float windowXScale = 1.0f; float windowYScale = 1.0f; }; /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to Loading Loading @@ -95,20 +111,35 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset = 0.0f; float yOffset = 0.0f; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; float windowXScale = 1.0f; float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds{}; BitSet32 pointerIds; // The data is stored by the pointerId. Use the marked bits in pointerIds to look up PointerInfo // per pointerId. PointerInfo pointerInfos[MAX_POINTERS]; void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, float windowYScale); void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, float windowYScale); /** * Returns whether the default pointer information should be used. This will be true when the * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors * and non splittable windows since we want all pointers for the EventEntry to go to this * target. */ bool useDefaultPointerInfo() const; /** * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is * true. */ const PointerInfo& getDefaultPointerInfo() const; }; std::string dispatchModeToString(int32_t dispatchMode); Loading services/inputflinger/tests/InputDispatcher_test.cpp +103 −0 Original line number Diff line number Diff line Loading @@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { mWindow1->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } } // namespace android::inputdispatcher Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +106 −48 Original line number Diff line number Diff line Loading @@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, EventEntry* eventEntry, int32_t inputTargetFlags) { if (inputTarget.useDefaultPointerInfo()) { const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); return std::make_unique<DispatchEntry>(eventEntry, // increments ref inputTargetFlags, pointerInfo.xOffset, pointerInfo.yOffset, inputTarget.globalScaleFactor, pointerInfo.windowXScale, pointerInfo.windowYScale); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); PointerCoords pointerCoords[MAX_POINTERS]; // Use the first pointer information to normalize all other pointers. This could be any pointer // as long as all other pointers are normalized to the same value and the final DispatchEntry // uses the offset and scale for the normalized pointer. const PointerInfo& firstPointerInfo = inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; // Iterate through all pointers in the event to normalize against the first. for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; // The scale factor is the ratio of the current pointers scale to the normalized scale. float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); // First apply the current pointers offset to set the window at 0,0 pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); // Next scale the coordinates. pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); // Lastly, offset the coordinates so they're in the normalized pointer's frame. pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, -firstPointerInfo.yOffset); } MotionEntry* combinedMotionEntry = new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId, motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, motionEntry.action, motionEntry.actionButton, motionEntry.flags, motionEntry.metaState, motionEntry.buttonState, motionEntry.classification, motionEntry.edgeFlags, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, 0 /* yOffset */); return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref inputTargetFlags, firstPointerInfo.xOffset, firstPointerInfo.yOffset, inputTarget.globalScaleFactor, firstPointerInfo.windowXScale, firstPointerInfo.windowYScale); } // --- InputDispatcherThread --- class InputDispatcher::InputDispatcherThread : public Thread { Loading Loading @@ -1111,7 +1172,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { if (DEBUG_FOCUS) { ALOGD("Dropping event delivery to target with channel '%s' because it " Loading Loading @@ -1785,23 +1846,34 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { return inputTarget.inputChannel->getConnectionToken() == windowHandle->getToken(); }); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { InputTarget inputTarget; sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); if (inputChannel == nullptr) { ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); return; } inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } const InputWindowInfo* windowInfo = windowHandle->getInfo(); InputTarget target; target.inputChannel = inputChannel; target.flags = targetFlags; target.xOffset = -windowInfo->frameLeft; target.yOffset = -windowInfo->frameTop; target.globalScaleFactor = windowInfo->globalScaleFactor; target.windowXScale = windowInfo->windowXScale; target.windowYScale = windowInfo->windowYScale; target.pointerIds = pointerIds; inputTargets.push_back(target); ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, windowInfo->windowXScale, windowInfo->windowYScale); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, Loading @@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; target.xOffset = xOffset; target.yOffset = yOffset; target.pointerIds.clear(); target.globalScaleFactor = 1.0f; target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); inputTargets.push_back(target); } Loading Loading @@ -2045,7 +2114,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", Loading @@ -2056,9 +2125,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " "windowScaleFactor=(%f, %f), pointerIds=0x%x", connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, inputTarget->windowYScale, inputTarget->pointerIds.value); connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset, inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale, inputTarget.windowYScale, inputTarget.pointerIds.value); #endif // Skip this event if the connection status is not normal. Loading @@ -2072,13 +2141,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. if (inputTarget->flags & InputTarget::FLAG_SPLIT) { if (inputTarget.flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) { if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { MotionEntry* splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); if (!splitMotionEntry) { return; // split event was dropped } Loading @@ -2100,7 +2169,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 Loading Loading @@ -2133,7 +2202,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, const InputTarget& inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", Loading @@ -2141,7 +2210,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } int32_t inputTargetFlags = inputTarget->flags; int32_t inputTargetFlags = inputTarget.flags; if (!(inputTargetFlags & dispatchMode)) { return; } Loading @@ -2149,11 +2218,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, inputTarget->windowYScale); std::unique_ptr<DispatchEntry> dispatchEntry = createDispatchEntry(inputTarget, eventEntry, inputTargetFlags); // Apply target flags and update the connection's input state. switch (eventEntry->type) { Loading @@ -2168,7 +2234,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName().c_str()); #endif delete dispatchEntry; return; // skip the inconsistent event } break; Loading Loading @@ -2215,12 +2280,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio "event", connection->getInputChannelName().c_str()); #endif delete dispatchEntry; return; // skip the inconsistent event } dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget->inputChannel->getConnectionToken()); inputTarget.inputChannel->getConnectionToken()); break; } Loading @@ -2238,7 +2302,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } // Enqueue the dispatch entry. connection->outboundQueue.push_back(dispatchEntry); connection->outboundQueue.push_back(dispatchEntry.release()); traceOutboundQueueLength(connection); } Loading Loading @@ -2611,21 +2675,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; target.yOffset = -windowInfo->frameTop; target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, windowInfo->windowXScale, windowInfo->windowYScale); target.globalScaleFactor = windowInfo->globalScaleFactor; target.windowXScale = windowInfo->windowXScale; target.windowYScale = windowInfo->windowYScale; } else { target.xOffset = 0; target.yOffset = 0; target.globalScaleFactor = 1.0f; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref &target, InputTarget::FLAG_DISPATCH_AS_IS); target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } Loading
services/inputflinger/dispatcher/InputDispatcher.h +3 −3 Original line number Diff line number Diff line Loading @@ -378,13 +378,13 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) EventEntry* eventEntry, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) const InputTarget& inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); Loading
services/inputflinger/dispatcher/InputTarget.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,43 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, float windowXScale, float windowYScale) { // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors // and non splittable windows since we will just use all the pointers from the input event. if (newPointerIds.isEmpty()) { setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); return; } // Ensure that the new set of pointers doesn't overlap with the current set of pointers. ALOG_ASSERT((pointerIds & newPointerIds) == 0); pointerIds |= newPointerIds; while (!newPointerIds.isEmpty()) { int32_t pointerId = newPointerIds.clearFirstMarkedBit(); pointerInfos[pointerId].xOffset = xOffset; pointerInfos[pointerId].yOffset = yOffset; pointerInfos[pointerId].windowXScale = windowXScale; pointerInfos[pointerId].windowYScale = windowYScale; } } void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, float windowYScale) { pointerIds.clear(); pointerInfos[0].xOffset = xOffset; pointerInfos[0].yOffset = yOffset; pointerInfos[0].windowXScale = windowXScale; pointerInfos[0].windowYScale = windowYScale; } bool InputTarget::useDefaultPointerInfo() const { return pointerIds.isEmpty(); } const PointerInfo& InputTarget::getDefaultPointerInfo() const { return pointerInfos[0]; } } // namespace android::inputdispatcher
services/inputflinger/dispatcher/InputTarget.h +39 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,22 @@ namespace android::inputdispatcher { /* * Information about each pointer for an InputTarget. This includes offset and scale so * all pointers can be normalized to a single offset and scale. */ struct PointerInfo { // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset = 0.0f; float yOffset = 0.0f; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float windowXScale = 1.0f; float windowYScale = 1.0f; }; /* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to Loading Loading @@ -95,20 +111,35 @@ struct InputTarget { // Flags for the input target. int32_t flags = 0; // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset = 0.0f; float yOffset = 0.0f; // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) float globalScaleFactor = 1.0f; float windowXScale = 1.0f; float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds{}; BitSet32 pointerIds; // The data is stored by the pointerId. Use the marked bits in pointerIds to look up PointerInfo // per pointerId. PointerInfo pointerInfos[MAX_POINTERS]; void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, float windowYScale); void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, float windowYScale); /** * Returns whether the default pointer information should be used. This will be true when the * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors * and non splittable windows since we want all pointers for the EventEntry to go to this * target. */ bool useDefaultPointerInfo() const; /** * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is * true. */ const PointerInfo& getDefaultPointerInfo() const; }; std::string dispatchModeToString(int32_t dispatchMode); Loading
services/inputflinger/tests/InputDispatcher_test.cpp +103 −0 Original line number Diff line number Diff line Loading @@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { mWindow1->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); touchedPoints.emplace_back(PointF{150, 150}); expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); // Consuming from window1 since it's the window that has the InputReceiver consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); mDispatcher->notifyMotion(&motionArgs); consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } } // namespace android::inputdispatcher