[Android 13]Input系列--获取触摸窗口
hongxi.zhu 2023-7-25
Android 13
InputDispatcher::dispatchMotionLocked
cpp
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> 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
cpp
InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& 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<sp<WindowInfoHandle>> 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<WindowInfoHandle>& 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<WindowInfoHandle> 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<WindowInfoHandle> 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> 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<WindowInfoHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle &&
foregroundWindowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& 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 < tempTouchState.windows.size();) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
touchedWindow.pointerIds.clearBit(pointerId);
if (touchedWindow.pointerIds.isEmpty()) {
tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
}
}
i += 1;
}
}
}
// Save changes unless the action was scroll in which case the temporary touch
// state was only valid for this one action.
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
if (tempTouchState.displayId >= 0) {
mTouchStatesByDisplay[displayId] = tempTouchState;
} else {
mTouchStatesByDisplay.erase(displayId);
}
}
// Update hover state.
mLastHoverWindowHandle = newHoverWindowHandle;
}
return injectionResult;
}
InputDispatcher::findTouchedWindowAtLocked
cpp
sp<WindowInfoHandle> 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<WindowInfoHandle>& 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
cpp
const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
int32_t displayId) const {
static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
auto it = mWindowHandlesByDisplay.find(displayId);
return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
}
那mWindowHandlesByDisplay
又是谁来添加插入数据的
cpp
void InputDispatcher::updateWindowHandlesForDisplayLocked(
const std::vector<sp<WindowInfoHandle>>& 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<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId);
std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById;
for (const sp<WindowInfoHandle>& handle : oldHandles) {
oldHandlesById[handle->getId()] = handle;
}
std::vector<sp<WindowInfoHandle>> newHandles;
for (const sp<WindowInfoHandle>& 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<WindowInfoHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
} else {
newHandles.push_back(handle);
}
}
// Insert or replace
// 将<displayId, newHandles>插入mWindowHandlesByDisplay
mWindowHandlesByDisplay[displayId] = newHandles;
}
谁调用的updateWindowHandlesForDisplayLocked
InputDispatcher::setInputWindowsLocked
cpp
void InputDispatcher::setInputWindowsLocked(
const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
if (DEBUG_FOCUS) {
std::string windowList;
for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
windowList += iwh->getName() + " ";
}
ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
}
// Check preconditions for new input windows
for (const sp<WindowInfoHandle>& 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<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
// Save the old windows' orientation by ID before it gets updated.
std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
oldWindowOrientations.emplace(handle->getId(),
handle->getInfo()->transform.getOrientation());
}
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); //更新并插入mWindowHandlesByDisplay
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
if (mLastHoverWindowHandle &&
std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
windowHandles.end()) {
mLastHoverWindowHandle = nullptr;
}
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
onFocusChangedLocked(*changes);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
mTouchStatesByDisplay.find(displayId);
if (stateIt != mTouchStatesByDisplay.end()) {
TouchState& state = stateIt->second;
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);
}
std::shared_ptr<InputChannel> 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<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
if (wallpaper != nullptr) {
sp<Connection> 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<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
if (newWindowHandle != nullptr &&
newWindowHandle->getInfo()->transform.getOrientation() !=
oldWindowOrientations[oldWindowHandle->getId()]) {
std::shared_ptr<InputChannel> 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<WindowInfoHandle>& 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
cpp
void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
const std::vector<DisplayInfo>& displayInfos) {
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
for (const auto& info : windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
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
cpp
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
const std::vector<gui::WindowInfo>& windowInfos,
const std::vector<DisplayInfo>& displayInfos) {
mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
}
看起来是个监听器的回调,应该是给别的地方持有这个监听器,寻找下这个DispatcherWindowListener
的实例化
InputDispatcher::InputDispatcher
cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& 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
cpp
status_t SurfaceFlinger::addWindowInfosListener(
const sp<IWindowInfosListener>& windowInfosListener) const {
mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
return NO_ERROR;
}
WindowInfosListenerInvoker::addWindowInfosListener
cpp
void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
sp<IBinder> 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
cpp
void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
const std::vector<DisplayInfo>& displayInfos,
bool shouldSync) {
ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> 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
cpp
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
cpp
void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
mInputManager->getDispatcher().displayRemoved(displayId);
}
nativeDisplayRemoved
cpp
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
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
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
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
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对窗口管理的内容了,不是本次探索的目的,到这里就明白当事件分发前寻找的触摸目标窗口信息是谁来告知的。