Loading services/inputflinger/dispatcher/InputDispatcher.cpp +147 −125 Original line number Diff line number Diff line Loading @@ -634,11 +634,10 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, const MotionEntry& entry) { std::vector<TouchedWindow> out; const int32_t maskedAction = MotionEvent::getActionMasked(entry.action); if (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER && maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { // Not a hover event - don't need to do anything return out; if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { // ACTION_SCROLL events should not affect the hovering pointer dispatch return {}; } // We should consider all hovering pointers here. But for now, just use the first one Loading Loading @@ -1315,7 +1314,8 @@ std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked( if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(pointerId); addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds, addPointerWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds, /*firstDownTimeInTarget=*/std::nullopt, outsideTargets); } } Loading Loading @@ -1821,7 +1821,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key std::vector<InputTarget> inputTargets; addWindowTargetLocked(focusedWindow, InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, getDownTime(*entry), inputTargets); getDownTime(*entry), inputTargets); // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); Loading Loading @@ -1905,7 +1905,6 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // Identify targets. std::vector<InputTarget> inputTargets; bool conflictingPointerActions = false; InputEventInjectionResult injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) Loading @@ -1917,8 +1916,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< } inputTargets = findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions, /*byref*/ injectionResult); findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult); LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED && !inputTargets.empty()); } else { Loading @@ -1930,7 +1928,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< addWindowTargetLocked(focusedWindow, InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, getDownTime(*entry), inputTargets); getDownTime(*entry), inputTargets); } } if (injectionResult == InputEventInjectionResult::PENDING) { Loading @@ -1954,11 +1952,6 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); // Dispatch the motion. if (conflictingPointerActions) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } dispatchEventLocked(currentTime, entry, inputTargets); return true; } Loading Loading @@ -2258,7 +2251,7 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( } std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, nsecs_t currentTime, const MotionEntry& entry, InputEventInjectionResult& outInjectionResult) { ATRACE_CALL(); Loading @@ -2283,20 +2276,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } bool isSplit = shouldSplitTouch(tempTouchState, entry); bool switchedDevice = false; if (oldState != nullptr) { std::set<int32_t> oldActiveDevices = oldState->getActiveDeviceIds(); const bool anotherDeviceIsActive = oldActiveDevices.count(entry.deviceId) == 0 && !oldActiveDevices.empty(); switchedDevice |= anotherDeviceIsActive; } const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); // A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any // touchable windows. const bool wasDown = oldState != nullptr && oldState->isDown(); const bool wasDown = oldState != nullptr && oldState->isDown(entry.deviceId); const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) || (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown); const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || Loading @@ -2304,34 +2290,19 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE; const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); // If pointers are already down, let's finish the current gesture and ignore the new events // from another device. However, if the new event is a down event, let's cancel the current // touch and let the new one take over. if (switchedDevice && wasDown && !isDown) { LOG(INFO) << "Dropping event because a pointer for another device " << " is already down in display " << displayId << ": " << entry.getDescription(); // TODO(b/211379801): test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; return {}; // wrong device } if (newGesture) { // If a new gesture is starting, clear the touch state completely. tempTouchState.reset(); isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " "in display %" PRId32, displayId); // TODO(b/211379801): test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; return {}; // wrong device } if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) { // Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated. tempTouchState.clearHoveringPointers(entry.deviceId); } if (isHoverAction) { // For hover actions, we will treat 'tempTouchState' as a new state, so let's erase // all of the existing hovering pointers and recompute. tempTouchState.clearHoveringPointers(); tempTouchState.clearHoveringPointers(entry.deviceId); } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { Loading Loading @@ -2402,8 +2373,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( continue; } if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { if (isHoverAction) { // The "windowHandle" is the target of this hovering pointer. tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId); } Loading @@ -2426,31 +2396,24 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } // Update the temporary touch state. std::bitset<MAX_POINTER_ID + 1> pointerIds; if (!isHoverAction) { std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(pointerId); } const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN; // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would // still add a window to the touch state. We should avoid doing that, but some of the // later checks ("at least one foreground window") rely on this in order to dispatch // the event properly, so that needs to be updated, possibly by looking at InputTargets. tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds, tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds, isDownOrPointerDown ? std::make_optional(entry.eventTime) : std::nullopt); // If this is the pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper // engine only supports touch events. We would need to add a mechanism similar // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (isDownOrPointerDown) { if (targetFlags.test(InputTarget::Flags::FOREGROUND) && // then also add the touched wallpaper windows so they are locked in for the // duration of the touch gesture. We do not collect wallpapers during HOVER_MOVE or // SCROLL because the wallpaper engine only supports touch events. We would need to // add a mechanism similar to View.onGenericMotionEvent to enable wallpapers to // handle these events. if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) && windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle); Loading Loading @@ -2488,8 +2451,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { LOG(INFO) << "Dropping event because the pointer is not down or we previously " if (!tempTouchState.isDown(entry.deviceId) && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId << " is not down or we previously " "dropped the pointer down event in display " << displayId << ": " << entry.getDescription(); outInjectionResult = InputEventInjectionResult::FAILED; Loading Loading @@ -2538,7 +2503,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (newTouchedWindowHandle != nullptr && !haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) { ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, ALOGI("Touch is slipping out of window %s into window %s in display %" PRId32, oldTouchedWindowHandle->getName().c_str(), newTouchedWindowHandle->getName().c_str(), displayId); Loading @@ -2549,9 +2514,11 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( const TouchedWindow& touchedWindow = tempTouchState.getTouchedWindow(oldTouchedWindowHandle); addWindowTargetLocked(oldTouchedWindowHandle, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); addPointerWindowTargetLocked(oldTouchedWindowHandle, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); // Make a slippery entrance into the new window. if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { Loading Loading @@ -2664,15 +2631,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Output targets from the touch state. for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (!touchedWindow.hasTouchingPointers(entry.deviceId) && !touchedWindow.hasHoveringPointers(entry.deviceId)) { // Windows with hovering pointers are getting persisted inside TouchState. // Do not send this event to those windows. std::bitset<MAX_POINTER_ID + 1> touchingPointers = touchedWindow.getTouchingPointers(entry.deviceId); if (touchingPointers.none()) { continue; } addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.getTouchingPointers(entry.deviceId), addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchingPointers, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); } Loading Loading @@ -2706,37 +2671,30 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } outInjectionResult = InputEventInjectionResult::SUCCEEDED; // Drop the outside or hover touch windows since we will not care about them // in the next iteration. tempTouchState.filterNonAsIsTouchWindows(); // Update final pieces of touch state if the injector had permission. if (switchedDevice) { if (DEBUG_FOCUS) { ALOGD("Conflicting pointer actions: Switched to a different device."); for (TouchedWindow& touchedWindow : tempTouchState.windows) { // Targets that we entered in a slippery way will now become AS-IS targets if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER); touchedWindow.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS; } *outConflictingPointerActions = true; } // Update final pieces of touch state if the injector had permission. if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->isDown()) { ALOGD_IF(DEBUG_FOCUS, "Conflicting pointer actions: Hover received while pointer was down."); *outConflictingPointerActions = true; if (oldState && oldState->isDown(entry.deviceId)) { // Started hovering, but the device is already down: reject the hover event LOG(ERROR) << "Got hover event " << entry.getDescription() << " but the device is already down " << oldState->dump(); outInjectionResult = InputEventInjectionResult::FAILED; return {}; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { // Pointer went up. tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id); } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. tempTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) { ALOGD("Conflicting pointer actions: Down received while already down or hovering."); *outConflictingPointerActions = true; } tempTouchState.removeAllPointersForDevice(entry.deviceId); } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { // One pointer went up. const int32_t pointerIndex = MotionEvent::getActionIndex(action); Loading Loading @@ -2885,7 +2843,6 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked( void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const { std::vector<InputTarget>::iterator it = Loading @@ -2907,8 +2864,54 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa it = inputTargets.end() - 1; } ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); LOG_ALWAYS_FATAL_IF(it->flags != targetFlags); LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); } void InputDispatcher::addPointerWindowTargetLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const REQUIRES(mLock) { if (pointerIds.none()) { for (const auto& target : inputTargets) { LOG(INFO) << "Target: " << target; } LOG(FATAL) << "No pointers specified for " << windowHandle->getName(); return; } std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { return inputTarget.inputChannel->getConnectionToken() == windowHandle->getToken(); }); // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that // causes a HOVER_EXIT to be generated. That means that the same entry of ACTION_DOWN would // have DISPATCH_AS_HOVER_EXIT and DISPATCH_AS_IS. And therefore, we have to create separate // input targets for hovering pointers and for touching pointers. // If we picked an existing input target above, but it's for HOVER_EXIT - let's use a new // target instead. if (it != inputTargets.end() && it->flags.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) { // Force the code below to create a new input target it = inputTargets.end(); } const WindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { std::optional<InputTarget> target = createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget); if (!target) { return; } inputTargets.push_back(*target); it = inputTargets.end() - 1; } LOG_ALWAYS_FATAL_IF(it->flags != targetFlags); LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); it->addPointers(pointerIds, windowInfo->transform); } Loading Loading @@ -3366,6 +3369,27 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } // Check if we need to cancel any of the ongoing gestures. We don't support multiple // devices being active at the same time in the same window, so if a new device is // active, cancel the gesture from the old device. std::unique_ptr<EventEntry> cancelEvent = connection->inputState .cancelConflictingInputStream(motionEntry, dispatchEntry->resolvedAction); if (cancelEvent != nullptr) { LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in " << connection->getInputChannelName() << " with event " << cancelEvent->getDescription(); std::unique_ptr<DispatchEntry> cancelDispatchEntry = createDispatchEntry(inputTarget, std::move(cancelEvent), InputTarget::Flags::DISPATCH_AS_IS); // Send these cancel events to the queue before sending the event from the new // device. connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry)); } if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { LOG(WARNING) << "channel " << connection->getInputChannelName() Loading Loading @@ -3975,7 +3999,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, keyEntry.downTime, targets); keyEntry.downTime, targets); } else { targets.emplace_back(fallbackTarget); } Loading @@ -3994,8 +4018,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds, motionEntry.downTime, targets); addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds, motionEntry.downTime, targets); } else { targets.emplace_back(fallbackTarget); const auto it = mDisplayInfos.find(motionEntry.displayId); Loading Loading @@ -4069,7 +4093,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } addWindowTargetLocked(windowHandle, targetFlags, pointerIds, addPointerWindowTargetLocked(windowHandle, targetFlags, pointerIds, motionEntry.downTime, targets); } else { targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel, Loading Loading @@ -5139,10 +5163,8 @@ void InputDispatcher::setInputWindowsLocked( for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); } LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; std::shared_ptr<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { Loading Loading @@ -5973,9 +5995,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { return BAD_VALUE; } std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds(); if (deviceIds.size() != 1) { LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds) << " in window: " << windowPtr->dump(); if (deviceIds.empty()) { LOG(WARNING) << "Can't pilfer: no touching devices in window: " << windowPtr->dump(); return BAD_VALUE; } Loading Loading @@ -6818,10 +6839,11 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl if (oldWallpaper != nullptr) { const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper); addWindowTargetLocked(oldWallpaper, addPointerWindowTargetLocked(oldWallpaper, oldTouchedWindow.targetFlags | InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets); pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets); state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper); } Loading Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +147 −125 Original line number Diff line number Diff line Loading @@ -634,11 +634,10 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, const MotionEntry& entry) { std::vector<TouchedWindow> out; const int32_t maskedAction = MotionEvent::getActionMasked(entry.action); if (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER && maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { // Not a hover event - don't need to do anything return out; if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { // ACTION_SCROLL events should not affect the hovering pointer dispatch return {}; } // We should consider all hovering pointers here. But for now, just use the first one Loading Loading @@ -1315,7 +1314,8 @@ std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked( if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(pointerId); addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds, addPointerWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds, /*firstDownTimeInTarget=*/std::nullopt, outsideTargets); } } Loading Loading @@ -1821,7 +1821,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key std::vector<InputTarget> inputTargets; addWindowTargetLocked(focusedWindow, InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, getDownTime(*entry), inputTargets); getDownTime(*entry), inputTargets); // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); Loading Loading @@ -1905,7 +1905,6 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // Identify targets. std::vector<InputTarget> inputTargets; bool conflictingPointerActions = false; InputEventInjectionResult injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) Loading @@ -1917,8 +1916,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< } inputTargets = findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions, /*byref*/ injectionResult); findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult); LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED && !inputTargets.empty()); } else { Loading @@ -1930,7 +1928,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< addWindowTargetLocked(focusedWindow, InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, getDownTime(*entry), inputTargets); getDownTime(*entry), inputTargets); } } if (injectionResult == InputEventInjectionResult::PENDING) { Loading @@ -1954,11 +1952,6 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); // Dispatch the motion. if (conflictingPointerActions) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } dispatchEventLocked(currentTime, entry, inputTargets); return true; } Loading Loading @@ -2258,7 +2251,7 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( } std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, nsecs_t currentTime, const MotionEntry& entry, InputEventInjectionResult& outInjectionResult) { ATRACE_CALL(); Loading @@ -2283,20 +2276,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } bool isSplit = shouldSplitTouch(tempTouchState, entry); bool switchedDevice = false; if (oldState != nullptr) { std::set<int32_t> oldActiveDevices = oldState->getActiveDeviceIds(); const bool anotherDeviceIsActive = oldActiveDevices.count(entry.deviceId) == 0 && !oldActiveDevices.empty(); switchedDevice |= anotherDeviceIsActive; } const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); // A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any // touchable windows. const bool wasDown = oldState != nullptr && oldState->isDown(); const bool wasDown = oldState != nullptr && oldState->isDown(entry.deviceId); const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) || (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown); const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || Loading @@ -2304,34 +2290,19 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE; const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); // If pointers are already down, let's finish the current gesture and ignore the new events // from another device. However, if the new event is a down event, let's cancel the current // touch and let the new one take over. if (switchedDevice && wasDown && !isDown) { LOG(INFO) << "Dropping event because a pointer for another device " << " is already down in display " << displayId << ": " << entry.getDescription(); // TODO(b/211379801): test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; return {}; // wrong device } if (newGesture) { // If a new gesture is starting, clear the touch state completely. tempTouchState.reset(); isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " "in display %" PRId32, displayId); // TODO(b/211379801): test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; return {}; // wrong device } if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) { // Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated. tempTouchState.clearHoveringPointers(entry.deviceId); } if (isHoverAction) { // For hover actions, we will treat 'tempTouchState' as a new state, so let's erase // all of the existing hovering pointers and recompute. tempTouchState.clearHoveringPointers(); tempTouchState.clearHoveringPointers(entry.deviceId); } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { Loading Loading @@ -2402,8 +2373,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( continue; } if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { if (isHoverAction) { // The "windowHandle" is the target of this hovering pointer. tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId); } Loading @@ -2426,31 +2396,24 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } // Update the temporary touch state. std::bitset<MAX_POINTER_ID + 1> pointerIds; if (!isHoverAction) { std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(pointerId); } const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN; // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would // still add a window to the touch state. We should avoid doing that, but some of the // later checks ("at least one foreground window") rely on this in order to dispatch // the event properly, so that needs to be updated, possibly by looking at InputTargets. tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds, tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId, pointerIds, isDownOrPointerDown ? std::make_optional(entry.eventTime) : std::nullopt); // If this is the pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper // engine only supports touch events. We would need to add a mechanism similar // to View.onGenericMotionEvent to enable wallpapers to handle these events. if (isDownOrPointerDown) { if (targetFlags.test(InputTarget::Flags::FOREGROUND) && // then also add the touched wallpaper windows so they are locked in for the // duration of the touch gesture. We do not collect wallpapers during HOVER_MOVE or // SCROLL because the wallpaper engine only supports touch events. We would need to // add a mechanism similar to View.onGenericMotionEvent to enable wallpapers to // handle these events. if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) && windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle); Loading Loading @@ -2488,8 +2451,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { LOG(INFO) << "Dropping event because the pointer is not down or we previously " if (!tempTouchState.isDown(entry.deviceId) && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId << " is not down or we previously " "dropped the pointer down event in display " << displayId << ": " << entry.getDescription(); outInjectionResult = InputEventInjectionResult::FAILED; Loading Loading @@ -2538,7 +2503,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( if (newTouchedWindowHandle != nullptr && !haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) { ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, ALOGI("Touch is slipping out of window %s into window %s in display %" PRId32, oldTouchedWindowHandle->getName().c_str(), newTouchedWindowHandle->getName().c_str(), displayId); Loading @@ -2549,9 +2514,11 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( const TouchedWindow& touchedWindow = tempTouchState.getTouchedWindow(oldTouchedWindowHandle); addWindowTargetLocked(oldTouchedWindowHandle, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); addPointerWindowTargetLocked(oldTouchedWindowHandle, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); // Make a slippery entrance into the new window. if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { Loading Loading @@ -2664,15 +2631,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // Output targets from the touch state. for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (!touchedWindow.hasTouchingPointers(entry.deviceId) && !touchedWindow.hasHoveringPointers(entry.deviceId)) { // Windows with hovering pointers are getting persisted inside TouchState. // Do not send this event to those windows. std::bitset<MAX_POINTER_ID + 1> touchingPointers = touchedWindow.getTouchingPointers(entry.deviceId); if (touchingPointers.none()) { continue; } addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.getTouchingPointers(entry.deviceId), addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchingPointers, touchedWindow.getDownTimeInTarget(entry.deviceId), targets); } Loading Loading @@ -2706,37 +2671,30 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } outInjectionResult = InputEventInjectionResult::SUCCEEDED; // Drop the outside or hover touch windows since we will not care about them // in the next iteration. tempTouchState.filterNonAsIsTouchWindows(); // Update final pieces of touch state if the injector had permission. if (switchedDevice) { if (DEBUG_FOCUS) { ALOGD("Conflicting pointer actions: Switched to a different device."); for (TouchedWindow& touchedWindow : tempTouchState.windows) { // Targets that we entered in a slippery way will now become AS-IS targets if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) { touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER); touchedWindow.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS; } *outConflictingPointerActions = true; } // Update final pieces of touch state if the injector had permission. if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->isDown()) { ALOGD_IF(DEBUG_FOCUS, "Conflicting pointer actions: Hover received while pointer was down."); *outConflictingPointerActions = true; if (oldState && oldState->isDown(entry.deviceId)) { // Started hovering, but the device is already down: reject the hover event LOG(ERROR) << "Got hover event " << entry.getDescription() << " but the device is already down " << oldState->dump(); outInjectionResult = InputEventInjectionResult::FAILED; return {}; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { // Pointer went up. tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id); } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. tempTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) { ALOGD("Conflicting pointer actions: Down received while already down or hovering."); *outConflictingPointerActions = true; } tempTouchState.removeAllPointersForDevice(entry.deviceId); } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { // One pointer went up. const int32_t pointerIndex = MotionEvent::getActionIndex(action); Loading Loading @@ -2885,7 +2843,6 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked( void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const { std::vector<InputTarget>::iterator it = Loading @@ -2907,8 +2864,54 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa it = inputTargets.end() - 1; } ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); LOG_ALWAYS_FATAL_IF(it->flags != targetFlags); LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); } void InputDispatcher::addPointerWindowTargetLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const REQUIRES(mLock) { if (pointerIds.none()) { for (const auto& target : inputTargets) { LOG(INFO) << "Target: " << target; } LOG(FATAL) << "No pointers specified for " << windowHandle->getName(); return; } std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { return inputTarget.inputChannel->getConnectionToken() == windowHandle->getToken(); }); // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that // causes a HOVER_EXIT to be generated. That means that the same entry of ACTION_DOWN would // have DISPATCH_AS_HOVER_EXIT and DISPATCH_AS_IS. And therefore, we have to create separate // input targets for hovering pointers and for touching pointers. // If we picked an existing input target above, but it's for HOVER_EXIT - let's use a new // target instead. if (it != inputTargets.end() && it->flags.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) { // Force the code below to create a new input target it = inputTargets.end(); } const WindowInfo* windowInfo = windowHandle->getInfo(); if (it == inputTargets.end()) { std::optional<InputTarget> target = createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget); if (!target) { return; } inputTargets.push_back(*target); it = inputTargets.end() - 1; } LOG_ALWAYS_FATAL_IF(it->flags != targetFlags); LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); it->addPointers(pointerIds, windowInfo->transform); } Loading Loading @@ -3366,6 +3369,27 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } // Check if we need to cancel any of the ongoing gestures. We don't support multiple // devices being active at the same time in the same window, so if a new device is // active, cancel the gesture from the old device. std::unique_ptr<EventEntry> cancelEvent = connection->inputState .cancelConflictingInputStream(motionEntry, dispatchEntry->resolvedAction); if (cancelEvent != nullptr) { LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in " << connection->getInputChannelName() << " with event " << cancelEvent->getDescription(); std::unique_ptr<DispatchEntry> cancelDispatchEntry = createDispatchEntry(inputTarget, std::move(cancelEvent), InputTarget::Flags::DISPATCH_AS_IS); // Send these cancel events to the queue before sending the event from the new // device. connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry)); } if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { LOG(WARNING) << "channel " << connection->getInputChannelName() Loading Loading @@ -3975,7 +3999,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, /*pointerIds=*/{}, keyEntry.downTime, targets); keyEntry.downTime, targets); } else { targets.emplace_back(fallbackTarget); } Loading @@ -3994,8 +4018,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds, motionEntry.downTime, targets); addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds, motionEntry.downTime, targets); } else { targets.emplace_back(fallbackTarget); const auto it = mDisplayInfos.find(motionEntry.displayId); Loading Loading @@ -4069,7 +4093,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } addWindowTargetLocked(windowHandle, targetFlags, pointerIds, addPointerWindowTargetLocked(windowHandle, targetFlags, pointerIds, motionEntry.downTime, targets); } else { targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel, Loading Loading @@ -5139,10 +5163,8 @@ void InputDispatcher::setInputWindowsLocked( for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); } LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; std::shared_ptr<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { Loading Loading @@ -5973,9 +5995,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { return BAD_VALUE; } std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds(); if (deviceIds.size() != 1) { LOG(WARNING) << "Can't pilfer. Currently touching devices: " << dumpSet(deviceIds) << " in window: " << windowPtr->dump(); if (deviceIds.empty()) { LOG(WARNING) << "Can't pilfer: no touching devices in window: " << windowPtr->dump(); return BAD_VALUE; } Loading Loading @@ -6818,10 +6839,11 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl if (oldWallpaper != nullptr) { const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper); addWindowTargetLocked(oldWallpaper, addPointerWindowTargetLocked(oldWallpaper, oldTouchedWindow.targetFlags | InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets); pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId), targets); state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper); } Loading