温馨提示:这篇文章已超过479天没有更新,请注意相关的内容是否还可用!
摘要:本文将介绍Android 13中Input系列的获取触摸窗口功能。通过深入了解,读者将了解如何获取和处理触摸事件,包括触摸点的位置、速度和方向等信息。本文将提供有关如何在Android应用程序中实现触摸窗口交互的实用指南,帮助开发者更好地理解和利用这一功能。
[Android 13]Input系列–获取触摸窗口
hongxi.zhu 2023-7-25
![[Android 13]Input系列--获取触摸窗口,Android 13中Input系列的触摸窗口获取方法 第1张 [Android 13]Input系列--获取触摸窗口,Android 13中Input系列的触摸窗口获取方法 第1张](http://www.857vps.cn/zb_users/upload/2024/04/20240401110736171194085611117.jpeg)
Android 13
InputDispatcher::dispatchMotionLocked
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ... if (isPointerEvent) { // Pointer event. (eg. touchscreen) // 获取触摸目标窗口 injectionResult = findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } else { // Non touch event. (eg. trackball) ... } ... dispatchEventLocked(currentTime, entry, inputTargets); return true; }
InputDispatcher::findTouchedWindowTargetsLocked
InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ... if (newGesture) { //如果是down事件(说明是一个新的触摸行为) bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; tempTouchState.reset(); tempTouchState.down = down; tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; tempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ... } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { // 如果是down事件 /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ int32_t x; int32_t y; const int32_t pointerIndex = getMotionEventActionPointerIndex(action); // Always dispatch mouse events to cursor position. if (isFromMouse) { x = int32_t(entry.xCursorPosition); y = int32_t(entry.yCursorPosition); } else { x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; const bool isStylus = isPointerFromStylus(entry, pointerIndex); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus, isDown /*addOutsideTargets*/); // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y, displayId); // Try to assign the pointer to the first foreground window we find, if there is one. newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected touch event: %s", (*err).c_str()); injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; newTouchedWindowHandle = nullptr; goto Failed; } // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr) { if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { // New window supports splitting, but we should never split mouse events. isSplit = !isFromMouse; } else if (isSplit) { // New window does not support splitting but we have already split events. // Ignore the new window. newTouchedWindowHandle = nullptr; } } else { // No window is touched, so set split to true. This will allow the next pointer down to // be delivered to a new window which supports split touch. Pointers from a mouse device // should never be split. tempTouchState.split = isSplit = !isFromMouse; } // Update hover state. if (newTouchedWindowHandle != nullptr) { if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) { newHoverWindowHandle = nullptr; } else if (isHoverAction) { newHoverWindowHandle = newTouchedWindowHandle; } } std::vector newTouchedWindows = findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); } if (newTouchedWindows.empty()) { ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.", x, y, displayId); injectionResult = InputEventInjectionResult::FAILED; goto Failed; } for (const sp& windowHandle : newTouchedWindows) { const WindowInfo& info = *windowHandle->getInfo(); // Skip spy window targets that are not valid for targeted injection. if (const auto err = verifyTargetedInjection(windowHandle, entry); err) { continue; } if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) { ALOGI("Not sending touch event to %s because it is paused", windowHandle->getName().c_str()); continue; } // Ensure the window has a connection and the connection is responsive const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle); if (!isResponsive) { ALOGW("Not sending touch gesture to %s because it is not responsive", windowHandle->getName().c_str()); continue; } // Drop events that can't be trusted due to occlusion if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) { TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(windowHandle, x, y); if (!isTouchTrustedLocked(occlusionInfo)) { if (DEBUG_TOUCH_OCCLUSION) { ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y); for (const auto& log : occlusionInfo.debugInfo) { ALOGD("%s", log.c_str()); } } sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage); if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) { ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid); continue; } } } // Drop touch events if requested by input feature if (shouldDropInput(entry, windowHandle)) { continue; } // Set target flags. int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS; if (canReceiveForegroundTouches(*windowHandle->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. targetFlags |= InputTarget::FLAG_FOREGROUND; } if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } if (isWindowObscuredAtPointLocked(windowHandle, x, y)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(windowHandle)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } // Update the temporary touch state. BitSet32 pointerIds; if (isSplit) { uint32_t pointerId = entry.pointerProperties[pointerIndex].id; pointerIds.markBit(pointerId); } tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); } } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. if (!tempTouchState.down) { if (DEBUG_FOCUS) { ALOGD("Dropping event because the pointer is not down or we previously " "dropped the pointer down event in display %" PRId32, displayId); } injectionResult = InputEventInjectionResult::FAILED; goto Failed; } addDragEventLocked(entry); // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && tempTouchState.isSlippery()) { const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/); sp oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus); // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { ALOGW("Dropping injected event: %s", (*err).c_str()); injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH; newTouchedWindowHandle = nullptr; goto Failed; } // Drop touch events if requested by input feature if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) { newTouchedWindowHandle = nullptr; } if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, oldTouchedWindowHandle->getName().c_str(), newTouchedWindowHandle->getName().c_str(), displayId); } // Make a slippery exit from the old window. tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); // Make a slippery entrance into the new window. if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { isSplit = !isFromMouse; } int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { targetFlags |= InputTarget::FLAG_FOREGROUND; } if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } else if (isWindowObscuredLocked(newTouchedWindowHandle)) { targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; } BitSet32 pointerIds; if (isSplit) { pointerIds.markBit(entry.pointerProperties[0].id); } tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } } } // Update dispatching for hover enter and exit. if (newHoverWindowHandle != mLastHoverWindowHandle) { // Let the previous window know that the hover sequence is over, unless we already did // it when dispatching it as is to newTouchedWindowHandle. if (mLastHoverWindowHandle != nullptr && (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT || mLastHoverWindowHandle != newTouchedWindowHandle)) { if (DEBUG_HOVER) { ALOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); } // Let the new window know that the hover sequence is starting, unless we already did it // when dispatching it as is to newTouchedWindowHandle. if (newHoverWindowHandle != nullptr && (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER || newHoverWindowHandle != newTouchedWindowHandle)) { if (DEBUG_HOVER) { ALOGD("Sending hover enter event to window %s.", newHoverWindowHandle->getName().c_str()); } tempTouchState.addOrUpdateWindow(newHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); } } // Ensure that we have at least one foreground window or at least one window that cannot be a // foreground target. If we only have windows that are not receiving foreground touches (e.g. we // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window // that is actually receiving the entire gesture. if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), [](const TouchedWindow& touchedWindow) { return !canReceiveForegroundTouches( *touchedWindow.windowHandle->getInfo()) || (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; })) { ALOGI("Dropping event because there is no touched window on display %d to receive it: %s", displayId, entry.getDescription().c_str()); injectionResult = InputEventInjectionResult::FAILED; goto Failed; } // Ensure that all touched windows are valid for injection. if (entry.injectionState != nullptr) { std::string errs; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { // Allow ACTION_OUTSIDE events generated by targeted injection to be // dispatched to any uid, since the coords will be zeroed out later. continue; } const auto err = verifyTargetedInjection(touchedWindow.windowHandle, entry); if (err) errs += "\n - " + *err; } if (!errs.empty()) { ALOGW("Dropping targeted injection: At least one touched window is not owned by uid " "%d:%s", *entry.injectionState->targetUid, errs.c_str()); injectionResult = InputEventInjectionResult::TARGET_MISMATCH; goto Failed; } } // Check whether windows listening for outside touches are owned by the same UID. If it is // set the policy flag that we will not reveal coordinate information to this window. if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { sp foregroundWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle) { const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { sp windowInfoHandle = touchedWindow.windowHandle; if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) { tempTouchState.addOrUpdateWindow(windowInfoHandle, InputTarget::FLAG_ZERO_COORDS, BitSet32(0)); } } } } } // If this is the first 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 (maskedAction == AMOTION_EVENT_ACTION_DOWN) { sp foregroundWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { const std::vector& windowHandles = getWindowHandlesLocked(displayId); for (const sp& windowHandle : windowHandles) { const WindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && windowHandle->getInfo()->inputConfig.test( WindowInfo::InputConfig::IS_WALLPAPER)) { tempTouchState .addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | InputTarget:: FLAG_WINDOW_IS_PARTIALLY_OBSCURED | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0)); } } } } // Success! Output targets. injectionResult = InputEventInjectionResult::SUCCEEDED; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.pointerIds, inputTargets); } // Drop the outside or hover touch windows since we will not care about them // in the next iteration. tempTouchState.filterNonAsIsTouchWindows(); Failed: // Update final pieces of touch state if the injector had permission. if (!wrongDevice) { if (switchedDevice) { if (DEBUG_FOCUS) { ALOGD("Conflicting pointer actions: Switched to a different device."); } *outConflictingPointerActions = true; } if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->down) { if (DEBUG_FOCUS) { ALOGD("Conflicting pointer actions: Hover received while pointer was " "down."); } *outConflictingPointerActions = true; } tempTouchState.reset(); if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; tempTouchState.displayId = displayId; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP || 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->down) { if (DEBUG_FOCUS) { ALOGD("Conflicting pointer actions: Down received while already down."); } *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { // One pointer went up. if (isSplit) { int32_t pointerIndex = getMotionEventActionPointerIndex(action); uint32_t pointerId = entry.pointerProperties[pointerIndex].id; for (size_t i = 0; i = 0) { mTouchStatesByDisplay[displayId] = tempTouchState; } else { mTouchStatesByDisplay.erase(displayId); } } // Update hover state. mLastHoverWindowHandle = newHoverWindowHandle; } return injectionResult; }
InputDispatcher::findTouchedWindowAtLocked
sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus, bool addOutsideTargets, bool ignoreDragWindow) { if (addOutsideTargets && touchState == nullptr) { LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets"); } // Traverse windows from front to back to find touched window. const auto& windowHandles = getWindowHandlesLocked(displayId); for (const sp& windowHandle : windowHandles) { if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) { continue; } const WindowInfo& info = *windowHandle->getInfo(); if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { return windowHandle; } if (addOutsideTargets && info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) { touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0)); } } return nullptr; }
InputDispatcher::getWindowHandlesLocked
const std::vector& InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { static const std::vector EMPTY_WINDOW_HANDLES; auto it = mWindowHandlesByDisplay.find(displayId); return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES; }
那mWindowHandlesByDisplay又是谁来添加插入数据的
![[Android 13]Input系列--获取触摸窗口,Android 13中Input系列的触摸窗口获取方法 第2张 [Android 13]Input系列--获取触摸窗口,Android 13中Input系列的触摸窗口获取方法 第2张](http://www.857vps.cn/zb_users/upload/2024/04/20240401110736171194085698437.jpeg)
void InputDispatcher::updateWindowHandlesForDisplayLocked( const std::vector& windowInfoHandles, int32_t displayId) { if (windowInfoHandles.empty()) { // Remove all handles on a display if there are no windows left. mWindowHandlesByDisplay.erase(displayId); return; } // 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& oldHandles = getWindowHandlesLocked(displayId); std::unordered_map oldHandlesById; for (const sp& handle : oldHandles) { oldHandlesById[handle->getId()] = handle; } std::vector newHandles; for (const sp& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); if (getInputChannelLocked(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = !info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) || !info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE); if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); continue; } } if (info->displayId != displayId) { ALOGE("Window %s updated by wrong display %d, should belong to display %d", handle->getName().c_str(), displayId, info->displayId); continue; } if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) && (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) { const sp& oldHandle = oldHandlesById.at(handle->getId()); oldHandle->updateFrom(handle); newHandles.push_back(oldHandle); } else { newHandles.push_back(handle); } } // Insert or replace // 将插入mWindowHandlesByDisplay mWindowHandlesByDisplay[displayId] = newHandles; }
谁调用的updateWindowHandlesForDisplayLocked
InputDispatcher::setInputWindowsLocked
void InputDispatcher::setInputWindowsLocked( const std::vector& windowInfoHandles, int32_t displayId) { if (DEBUG_FOCUS) { std::string windowList; for (const sp& iwh : windowInfoHandles) { windowList += iwh->getName() + " "; } ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } // Check preconditions for new input windows for (const sp& window : windowInfoHandles) { const WindowInfo& info = *window->getInfo(); // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL const bool noInputWindow = info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); if (noInputWindow && window->getToken() != nullptr) { ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", window->getName().c_str()); window->releaseChannel(); } // Ensure all spy windows are trusted overlays LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.inputConfig.test( WindowInfo::InputConfig::TRUSTED_OVERLAY), "%s has feature SPY, but is not a trusted overlay.", window->getName().c_str()); // Ensure all stylus interceptors are trusted overlays LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.inputConfig.test( WindowInfo::InputConfig::TRUSTED_OVERLAY), "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.", window->getName().c_str()); } // Copy old handles for release if they are no longer present. const std::vector oldWindowHandles = getWindowHandlesLocked(displayId); // Save the old windows' orientation by ID before it gets updated. std::unordered_map oldWindowOrientations; for (const sp& handle : oldWindowHandles) { oldWindowOrientations.emplace(handle->getId(), handle->getInfo()->transform.getOrientation()); } updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); //更新并插入mWindowHandlesByDisplay const std::vector& windowHandles = getWindowHandlesLocked(displayId); if (mLastHoverWindowHandle && std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == windowHandles.end()) { mLastHoverWindowHandle = nullptr; } std::optional changes = mFocusResolver.setInputWindows(displayId, windowHandles); if (changes) { onFocusChangedLocked(*changes); } std::unordered_map::iterator stateIt = mTouchStatesByDisplay.find(displayId); if (stateIt != mTouchStatesByDisplay.end()) { TouchState& state = stateIt->second; for (size_t i = 0; i getName().c_str(), displayId); } std::shared_ptr touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND && touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp wallpaper = state.getWallpaperWindow(); if (wallpaper != nullptr) { sp wallpaperConnection = getConnectionLocked(wallpaper->getToken()); if (wallpaperConnection != nullptr) { synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); } } } } state.windows.erase(state.windows.begin() + i); } else { ++i; } } // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We // could just clear the state here. if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId && std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) == windowHandles.end()) { ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str()); sendDropWindowCommandLocked(nullptr, 0, 0); mDragState.reset(); } } // Determine if the orientation of any of the input windows have changed, and cancel all // pointer events if necessary. for (const sp& oldWindowHandle : oldWindowHandles) { const sp newWindowHandle = getWindowHandleLocked(oldWindowHandle); if (newWindowHandle != nullptr && newWindowHandle->getInfo()->transform.getOrientation() != oldWindowOrientations[oldWindowHandle->getId()]) { std::shared_ptr inputChannel = getInputChannelLocked(newWindowHandle->getToken()); if (inputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window's orientation changed"); synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); } } } // Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (const sp& oldWindowHandle : oldWindowHandles) { if (getWindowHandleLocked(oldWindowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); } oldWindowHandle->releaseChannel(); } } }
谁来调用setInputWindowsLocked, 主要是两个地方:
- InputDispatcher::onWindowInfosChanged
- InputDispatcher::displayRemoved
途径一
InputDispatcher::onWindowInfosChanged
void InputDispatcher::onWindowInfosChanged(const std::vector& windowInfos, const std::vector& displayInfos) { // The listener sends the windows as a flattened array. Separate the windows by display for // more convenient parsing. std::unordered_map handlesPerDisplay; for (const auto& info : windowInfos) { handlesPerDisplay.emplace(info.displayId, std::vector()); handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info)); } { // acquire lock std::scoped_lock _l(mLock); mDisplayInfos.clear(); for (const auto& displayInfo : displayInfos) { mDisplayInfos.emplace(displayInfo.displayId, displayInfo); } for (const auto& [displayId, handles] : handlesPerDisplay) { setInputWindowsLocked(handles, displayId); } } // Wake up poll loop since it may need to make new input dispatching choices. // 唤醒dispatcher线程,处理窗口信息改变的相关事项 mLooper->wake(); }
寻找onWindowInfosChanged的调用地方
InputDispatcher::DispatcherWindowListener::onWindowInfosChanged
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged( const std::vector& windowInfos, const std::vector& displayInfos) { mDispatcher.onWindowInfosChanged(windowInfos, displayInfos); }
看起来是个监听器的回调,应该是给别的地方持有这个监听器,寻找下这个DispatcherWindowListener的实例化
InputDispatcher::InputDispatcher
InputDispatcher::InputDispatcher(const sp& policy, std::chrono::nanoseconds staleEventTimeout) : mPolicy(policy), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), // mInTouchMode will be initialized by the WindowManager to the default device config. // To avoid leaking stack in case that call never comes, and for tests, // initialize it here anyways. mInTouchMode(kDefaultInTouchMode), mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mWindowTokenWithPointerCapture(nullptr), mStaleEventTimeout(staleEventTimeout), mLatencyAggregator(), mLatencyTracker(&mLatencyAggregator) { mLooper = new Looper(false); mReporter = createInputReporter(); //实例化DispatcherWindowListener,并通过binder传给SurfaceComposerClient,也就是SurfaceFlinger mWindowInfoListener = new DispatcherWindowListener(*this); SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); ... }
到这里我们明白Android 13通过途径1(InputDispatcher::onWindowInfosChanged)来更新触摸窗口信息的发起者是SurfaceFlinger
SurfaceFlinger::addWindowInfosListener
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::addWindowInfosListener( const sp& windowInfosListener) const { mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener); return NO_ERROR; }
WindowInfosListenerInvoker::addWindowInfosListener
void WindowInfosListenerInvoker::addWindowInfosListener(sp listener) { sp asBinder = IInterface::asBinder(listener); asBinder->linkToDeath(this); std::scoped_lock lock(mListenersMutex); mWindowInfosListeners.try_emplace(asBinder, std::move(listener)); }
inputdispatcher实例化时调用上面流程跨进程向SurfaceFlinger注册了DispatcherWindowListener, 回到上面,我们找到回调这个监听器的onWindowInfosChanged的地方
WindowInfosListenerInvoker::windowInfosChanged
frameworks/native/services/surfaceflinger/WindowInfosListenerInvoker.cpp
void WindowInfosListenerInvoker::windowInfosChanged(const std::vector& windowInfos, const std::vector& displayInfos, bool shouldSync) { ftl::SmallVector windowInfosListeners; { std::scoped_lock lock(mListenersMutex); for (const auto& [_, listener] : mWindowInfosListeners) { windowInfosListeners.push_back(listener); } } mCallbacksPending = windowInfosListeners.size(); for (const auto& listener : windowInfosListeners) { //向所有向SurfaceFlinger注册了WindowInfosListener的进程回调onWindowInfosChanged //其中包括inputDispatcher中的onWindowInfosChanged listener->onWindowInfosChanged(windowInfos, displayInfos, shouldSync ? mWindowInfosReportedListener : nullptr); } }
到这里途径1(InputDispatcher::onWindowInfosChanged)来更新触摸窗口信息的流程就找到了。
途径二
InputDispatcher::displayRemoved
void InputDispatcher::displayRemoved(int32_t displayId) { { // acquire lock std::scoped_lock _l(mLock); // Set an empty list to remove all handles from the specific display. setInputWindowsLocked(/* window handles */ {}, displayId); setFocusedApplicationLocked(displayId, nullptr); // Call focus resolver to clean up stale requests. This must be called after input windows // have been removed for the removed display. mFocusResolver.displayRemoved(displayId); // Reset pointer capture eligibility, regardless of previous state. std::erase(mIneligibleDisplaysForPointerCapture, displayId); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); }
NativeInputManager::displayRemoved
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { mInputManager->getDispatcher().displayRemoved(displayId); }
nativeDisplayRemoved
static const JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ ... {"displayRemoved", "(I)V", (void*)nativeDisplayRemoved}, //动态注册函数表 }; static void nativeDisplayRemoved(JNIEnv* env, jobject nativeImplObj, jint displayId) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->displayRemoved(env, displayId); }
看这里就知道是java层调下来的, 那就到java层找找
NativeInputManagerService::displayRemoved
frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java
public interface NativeInputManagerService { ... void displayRemoved(int displayId); ... class NativeImpl implements NativeInputManagerService { ... public native void displayRemoved(int displayId); ... } }
InputManagerService::onDisplayRemoved
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
/** The system implementation of {@link IInputManager} that manages input devices. */ public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor { ... /** Clean up input window handles of the given display. */ public void onDisplayRemoved(int displayId) { if (mPointerIconDisplayContext != null && mPointerIconDisplayContext.getDisplay().getDisplayId() == displayId) { mPointerIconDisplayContext = null; } updateAdditionalDisplayInputProperties(displayId, AdditionalDisplayInputProperties::reset); mNative.displayRemoved(displayId); } ... }
InputMonitor::onDisplayRemoved
frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
void onDisplayRemoved() { mHandler.removeCallbacks(mUpdateInputWindows); mHandler.post(() -> { // Make sure any pending setInputWindowInfo transactions are completed. That prevents // the timing of updating input info of removed display after cleanup. mService.mTransactionFactory.get().syncInputWindows().apply(); // It calls InputDispatcher::setInputWindows directly. //mService是WindowManagerService,构造时向它传入了java层InputManagerService对象 mService.mInputManager.onDisplayRemoved(mDisplayId); }); mDisplayRemoved = true; }
DisplayContent::removeImmediately
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
@Override void removeImmediately() { mDeferredRemoval = false; try { // Clear all transitions & screen frozen states when removing display. ... mInputMonitor.onDisplayRemoved(); ... } finally { mDisplayReady = false; } // Apply the pending transaction here since we may not be able to reach the DisplayContent // on the next traversal if it's removed from RootWindowContainer child list. getPendingTransaction().apply(); mWmService.mWindowPlacerLocked.requestTraversal(); }
到这里差不多了,再往上查就是WMS对窗口管理的内容了,不是本次探索的目的,到这里就明白当事件分发前寻找的触摸目标窗口信息是谁来告知的。
还没有评论,来说两句吧...