前面一篇文章,分析了 InputReader 处理 motion event,经过对事件的加工,生成高级事件 NotifyMotionArgs。InputReader 最终会把事件发送给 InputDispatcher,如下
void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
if (debugInboundEventDetails()) {
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),
args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
args.downTime);
for (uint32_t i = 0; i < args.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
i, args.pointerProperties[i].id,
ftl::enum_string(args.pointerProperties[i].toolType).c_str(),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
// ...
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
// 先把 motion event 发送给策略,看看策略是否截断
// 如果截断了,在 policyFlags 中去掉 POLICY_FLAG_PASS_TO_USER
mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
// 策略处理的过慢,这里会有警告 log
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
}
bool needWake = false;
{ // acquire lock
mLock.lock();
// ...
// 创建 MotionEntry,包装 motion event 数据
std::unique_ptr<MotionEntry> newEntry =
std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
args.displayId, policyFlags, args.action,
args.actionButton, args.flags, args.metaState,
args.buttonState, args.classification, args.edgeFlags,
args.xPrecision, args.yPrecision,
args.xCursorPosition, args.yCursorPosition,
args.downTime, args.pointerCount,
args.pointerProperties, args.pointerCoords);
// ...
// MotionEntry 加入到 inbound queue
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
// 如果有必要,唤醒 InputDispatcher 线程,处理 inbound queue 中的事件
if (needWake) {
mLooper->wake();
}
}
InputDispatcher 收到 motion event 后
- 询问策略是否截断 motion event。
- motion event 保存到 InputDispatcher inbound queue 中,并唤醒 InputDispatcher 线程处理 inbound queue 中的 motion event。
截断策略
// com_android_server_input_InputManagerService.cpp
void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
uint32_t& policyFlags) {
ATRACE_CALL();
// 获取系统是否是交互状态
const bool interactive = mInteractive.load();
if (interactive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
// 来自触摸屏的 motion event,是受信任的,并且非注入的
if ((policyFlags & POLICY_FLAG_TRUSTED) == 0 || (policyFlags & POLICY_FLAG_INJECTED)) {
// ...
}
// 如果系统处于交互状态,策略不截断 motion event
if (policyFlags & POLICY_FLAG_INTERACTIVE) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
return;
}
JNIEnv* env = jniEnv();
// 如果系统处于非交互状态,询问上层策略是否截断 motion event
const jint wmActions =
env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
displayId, when, policyFlags);
if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) {
return;
}
// 处理上层策略返回的结果
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
uint32_t& policyFlags) {
if (wmActions & WM_ACTION_PASS_TO_USER) {
// 上层分发策略允许分发,policyFlags 中添加 POLICY_FLAG_PASS_TO_USER
policyFlags |= POLICY_FLAG_PASS_TO_USER;
} else {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("handleInterceptActions: Not passing key to user.");
#endif
}
}
对于 motion event 来说,如果系统处于交互状态,例如,显示屏处于亮屏状态,策略不截断。而如果系统处于非交互状态,例如,显示屏处于灭屏状态,需要询问上层策略是否截断,如下
// PhoneWindowManager.java
// 注意,这是在非交互状态下,决定是否分发 motion event
public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
int policyFlags) {
// 如果 motion event 唤醒设备,那么不需要分发,即策略截断了
if ((policyFlags & FLAG_WAKE) != 0) {
if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
// 0 表示截断了 motion event
return 0;
}
}
// 在设备处于非交互状态下,只有某些特殊情况,才允许分发 motion event
if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
return ACTION_PASS_TO_USER;
}
// theater mode 下,motion event 唤醒了设备,也不分发,即策略截断了
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
// 默认不允许分发 motion event
return 0;
}
private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
// Apply the default display policy to unknown displays as well.
final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
|| displayId == INVALID_DISPLAY;
final Display display = isDefaultDisplay
? mDefaultDisplay
: mDisplayManager.getDisplay(displayId);
final boolean displayOff = (display == null
|| display.getState() == STATE_OFF);
// 显示屏处于 OFF 状态,并且非手表设备,不分发 motion event,即截断
if (displayOff && !mHasFeatureWatch) {
return false;
}
// 显示屏处于非 OFF 状态,并且有锁屏,需要分发 motion event 给锁屏,即不截断
// Send events to keyguard while the screen is on and it's showing.
if (isKeyguardShowingAndNotOccluded() && !displayOff) {
return true;
}
// Watches handle BACK and hardware buttons specially
if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK
|| keyCode == KeyEvent.KEYCODE_STEM_PRIMARY
|| keyCode == KeyEvent.KEYCODE_STEM_1
|| keyCode == KeyEvent.KEYCODE_STEM_2
|| keyCode == KeyEvent.KEYCODE_STEM_3)) {
return false;
}
// dream 模式下,也需要分发 motion event,即不截断
// 因为显示屏上可能显示 AOD 组件
if (isDefaultDisplay) {
IDreamManager dreamManager = getDreamManager();
try {
if (dreamManager != null && dreamManager.isDreaming()) {
return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when checking if dreaming", e);
}
}
// 默认,不允许分发 motion event
return false;
}
对于 motion event 来说,当设备处于非交互状态
- 如果是手机/平板设备,并且显示屏处于 OFF 状态,motion event 不会分发,即策略截断了事件。
- 显示屏处于非 OFF 状态,例如, DOZE 状态,并且还有锁屏,那么 motion event 需要分发给锁屏,因此策略不截断事件。
- 如果系统处于 dream 模式下,例如,显示 AOD 组件,那么 motion event 需要分发给 AOD 组件,即策略不截断。
分发 motion event
motion event 与 key event 一样,InputDispatcher 都是通过线程循环进行分发。这里省略线程循环的代码,直接看分发 motion event 的方法,如下
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
if (!entry->dispatchInProgress) {
entry->dispatchInProgress = true;
// 这里会打印 motion event 数据
logOutboundMotionDetails("dispatchMotion - ", *entry);
}
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
setInjectionResult(*entry,
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
return true;
}
// true
const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
// inputTargets 保存的是要发送 motion event 的所有目标窗口
// Identify targets.
std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
if (mDragState &&
(entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
// ...
}
// 1. 获取触摸的窗口
inputTargets =
findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
/*byref*/ injectionResult);
LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
!inputTargets.empty());
} else {
// Non touch event. (eg. trackball)
// ...
}
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
setInjectionResult(*entry, injectionResult);
if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) {
return true;
}
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
CancelationOptions::Mode mode(
isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
: CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
CancelationOptions options(mode, "input event injection failed");
synthesizeCancelationEventsForMonitorsLocked(options);
return true;
}
// global monitor 窗口,也是 motion event 的目标窗口,这里不讨论
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
// Dispatch the motion.
if (conflictingPointerActions) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"conflicting pointer actions");
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
// 2. 分发 motion event 给所有目标窗口
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
寻找触摸的窗口
key event 是发送给焦点窗口,而对于单手指的 motion event, 无论是 down event,还是 move event 和 up event,都是发送给 down event 的 x,y 坐标落入的窗口。
因此,直接来分析如何为 motion down event 寻找触摸的窗口,而对于 motion move event 和 motion up event 的触摸窗口,留给读者自行分析。
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
// targets 保存 motion event 所有的目标窗口
std::vector<InputTarget> targets;
const int32_t displayId = entry.displayId;
const int32_t action = entry.action;
// 单手指的 down event 的 action 为 AMOTION_EVENT_ACTION_DOWN
const int32_t maskedAction = MotionEvent::getActionMasked(action);
outInjectionResult = InputEventInjectionResult::PENDING;
const TouchState* oldState = nullptr;
TouchState tempTouchState;
// 对 down event 来说,此时 mTouchStatesByDisplay 没有数据
if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
// ...
}
// ...
// false
const bool wasDown = oldState != nullptr && oldState->isDown();
// true
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
// true
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
// ...
if (newGesture) {
// If a new gesture is starting, clear the touch state completely.
tempTouchState.reset();
tempTouchState.deviceId = entry.deviceId;
tempTouchState.source = entry.source;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
// ...
}
// ...
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
// 解析 motion down event 的 x,y 坐标
const auto [x, y] = resolveTouchedPosition(entry);
// 从 action 中获取 pointer index
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// false
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
// false
const bool isFromCrossDevice = entry.flags & AMOTION_EVENT_FLAG_FROM_DEVICE_INTEGRATION_SERVICE;
// 1. 寻找触摸的窗口
// 返回的数据中,newTouchedWindowHandle 是 x,y 坐标落入的窗口(排除 spy window)
// outsideTargets 是所有监听 outside touch event 的窗口
auto [newTouchedWindowHandle, outsideTargets] =
findTouchedWindowAtLocked(displayId, x, y, isStylus, false /*ignoreDragWindow*/, isFromCrossDevice);
// targets 保存监听 outside touch event 的窗口
// 因为这种窗口的特性是,当 motion down event 落入窗口之外时,会收到 outside event
if (isDown) {
targets += outsideTargets;
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
// ...
}
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
// ...
}
// ...
// 获取 x,y 坐标落入的所有 spy windows
// 注意,这些 spy windows 必须在 newTouchedWindowHandle 之上
// spy window 的特性是,它会接收 motion event,同时也会把 motion event 传递给其下的窗口
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
if (newTouchedWindowHandle != nullptr) {
// newTouchedWindowHandle 加入到 spy windows 集合的队首位置,因为 motion event 要首先分发给它
// newTouchedWindows 现在保存是 motion event 要分发给的所有目标窗口
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
}
if (newTouchedWindows.empty()) {
// ...
return {};
}
// 2. 遍历所有的目标窗口,并用 tempTouchState.windows 收集有效的目标窗口
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
// 检测窗口是否能接收 motion event
// 例如,没有 input channel,不能接收 motion event
if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
continue;
}
// ...
// Set target flags.
ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
// Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND.
// 非 spy window,并且可以聚焦,就可以称之为接收 motion event 的前台窗口
// 例如,Activity 真窗,就是一个前台窗口
if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
// There should only be one touched window that can be "foreground" for the pointer.
targetFlags |= InputTarget::Flags::FOREGROUND;
}
// ...
// 检测遍历的目标窗口在 x,y 坐标处是否被遮挡,还是被完全遮挡
if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
} else if (isWindowObscuredLocked(windowHandle)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
// Update the temporary touch state.
// pointerIds 保存遍历的目标窗口的 pointer id
std::bitset<MAX_POINTER_ID + 1> pointerIds;
if (!isHoverAction) {
pointerIds.set(entry.pointerProperties[pointerIndex].id);
}
// true
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
// tempTouchState 保存目标窗口
// 其实就是创建 TouchedWindow,保存到 tempTouchState.windows 列表中
tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, 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) {
// 如果前台窗口需要显示壁纸,那么 tempTouchState 保存壁纸窗口,因为它也可以作为 motion event 的目标窗口
if (targetFlags.test(InputTarget::Flags::FOREGROUND) &&
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
if (wallpaper != nullptr) {
ftl::Flags<InputTarget::Flags> wallpaperFlags =
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED |
InputTarget::Flags::DISPATCH_AS_IS;
if (isSplit) {
wallpaperFlags |= InputTarget::Flags::SPLIT;
}
tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds,
entry.eventTime);
}
}
}
}
// ...
} else {
// ...
}
// ...
// 3. tempTouchState.windows 保存的是所有有效的目标窗口,现在为每一个窗口创建 InputTarget
// 然后保存到 targets
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
continue;
}
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
targets);
}
// ...
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
// tempTouchState.windows 只保存 target flags 为 DISPATCH_AS_IS 的窗口
// 也就是说,移除 outside touch windows
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
tempTouchState.filterNonAsIsTouchWindows();
// ...
// 4. 返回所有的目标
return targets;
}
为了更直观的分析,我将省略一些功能的代码分析,例如,split touch,outside touche event,spy window 等等,读者看下注释,了解下即可。
第一步,根据 motion down event 的 x,y 坐标,获取触摸的窗口,如下
std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
#ifdef DISABLE_DEVICE_INTEGRATION
bool ignoreDragWindow) const {
#else
bool ignoreDragWindow,
bool isFromCrossDevice) const {
#endif
// Traverse windows from front to back to find touched window.
std::vector<InputTarget> outsideTargets;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
continue;
}
#ifndef DISABLE_DEVICE_INTEGRATION
const WindowInfo* windowInfo = windowHandle->getInfo();
// Device Integration: bypass the event in black screen if it coming from DIS
bool bypassBlackScreen = (windowInfo->layoutParamsType == WindowInfo::Type::SYSTEM_BLACKSCREEN_OVERLAY)
&& isFromCrossDevice;
if (bypassBlackScreen) {
continue;
}
#endif
const WindowInfo& info = *windowHandle->getInfo();
// 排除 spy window,找到 x,y 坐标落入的窗口
if (!info.isSpy() &&
windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
// 从这里可以看出,outsideTargets 保存的窗口,是位于 windowHandle 之上
return {windowHandle, outsideTargets};
}
// 监听 outside touch event 的窗口,保存到 outsideTargets
// 例如 Dialog 就可以监听 outside touch event
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
/*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
outsideTargets);
}
}
return {nullptr, {}};
}
bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
// 同一个 display 下,并且窗口必须可见
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
return false;
}
// 触控笔相关
// false
const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();
// 窗口必须可触摸
// 例如,启动窗口不可触摸,所以启动窗口的 root view 不会收到 touch event
if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
return false;
}
// 检测 x,y 坐标是否在窗口可触摸区域
// displayTransform 的作用是把物理屏坐标转换为逻辑屏坐标
const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
const auto p = displayTransform.transform(x, y);
if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
return false;
}
return true;
}
第二步,遍历可以作为 motion down event 目标的窗口
- 过滤掉无法接收 motion event 的窗口。例如,启动窗口没有 input channel,它无法接收 motion event。
- 解析目标窗口的 flags,其中最重要的是解析前台窗口 flag,例如,Activity 真窗就是一个前台窗口。
- 把有效的目标窗口,保存到tempTouchState.windows 集合中。
// TouchState.cpp void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget) { for (TouchedWindow& touchedWindow : windows) { // ... } TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; touchedWindow.targetFlags = targetFlags; touchedWindow.pointerIds = pointerIds; touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget; windows.push_back(touchedWindow); } - 如果前台窗口显示了壁纸,那么 tempTouchState.windows 还会保存壁纸窗口。例如,Launcher 就需要显示壁纸,如果在 Launcher 窗口上触摸,壁纸窗口也可以作为 motion event 的目标窗口。
第三步,为 tempTouchState.windows 保存的每一个有效目标窗口,创建 InputTarget,并保存到 targets 中。
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 =
std::find_if(inputTargets.begin(), inputTargets.end(),
[&windowHandle](const InputTarget& inputTarget) {
return inputTarget.inputChannel->getConnectionToken() ==
windowHandle->getToken();
});
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
// 创建 InputTarget
std::optional<InputTarget> target =
createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
if (!target) {
return;
}
// 保存到 inputTargets
inputTargets.push_back(*target);
it = inputTargets.end() - 1;
}
ALOG_ASSERT(it->flags == targetFlags);
ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
// InputTarget::pointerTransforms 保存 pointer id 对应的 window transform
// TODO: windowInfo->transform 应该是把 logical display 坐标转化为窗口坐标
it->addPointers(pointerIds, windowInfo->transform);
}
第四步,至此 targets 保存了 motion down event 所有的目标窗口,返回给 InputDispatcher。
分发 motion event 给目标窗口
分发事件的流程都是一样的,无论是分发 key event,还是分发 motion event,都是为每一个目标窗口开启一个分发循环,把事件发送给窗口,然后等待窗口反馈处理结果。详细流程,请参考Andorid U Input系统:InputDispatcher 分发按键事件。