Android findFocusedWindowTargets
2024-03-13 本文已影响0人
付凯强
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
在以上文件中,不管是dispatchKeyLocked还是dispatchMotionLocked,都会通过调用findFocusedWindowTargetsLocked来校验FocusedWindow是否存在,即分发输入事件必须要有焦点窗口。
findFocusedWindowTargetsLocked
InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
std::string reason;
// 通过EventEntry获取displayId
int32_t displayId = getTargetDisplayId(entry);
// 通过displayId来获得代表焦点窗口的对象focusedWindowHandle,类型为InputWindowHandle
sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
// 通过mFocusedApplicationHandlesByDisplay以及displayId来获得代表焦点所在的应用
std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// If there is no currently focused window and no focused application
// then drop the event.
// 如果焦点窗口为空,焦点应用还为空
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
NamedEnum::string(entry.type).c_str(), displayId);
// 输出丢弃这次事件的日志,并返回InputEventInjectionResult::FAILED
return InputEventInjectionResult::FAILED;
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
// Only start counting when we have a focused event to dispatch. The ANR is canceled if we
// start interacting with another application via touch (app switch). This code can be removed
// if the "no focused window ANR" is moved to the policy. Input doesn't know whether
// an app is expected to have a focused window.
// 兼容性的行为:如果有焦点应用没有焦点窗口,则生成一个anr
// 当有焦点事件需要分发的时候才开始计时
// 当应用发生切换的时候,ANR会被取消
// 当有焦点应用却没有焦点窗口的时候,会执行以下代码:
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
// mNoFocusedWindowTimeoutTime 默认为空
if (!mNoFocusedWindowTimeoutTime.has_value()) {
// We just discovered that there's no focused window. Start the ANR timer
// 仅仅当没有焦点窗口的时候,我们才启动ANR的计时器
// 获取超时时间,默认为5s
std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
// mNoFocusedWindowTimeoutTime被设置为发生ANR的时间
mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
// 把焦点应用赋值给mAwaitedFocusedApplication
mAwaitedFocusedApplication = focusedApplicationHandle;
// 把现在事件分发的目标displayId赋值给mAwaitedApplicationDisplayId
mAwaitedApplicationDisplayId = displayId;
// 进程启动完毕后有可能会添加一个窗口,所以等候5s钟,即5s钟后再次check。
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
// 把ANR发生的时间赋值给nextWakeupTime,即先休眠,在ANR发生的时间时再check一次
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
// 返回InputEventInjectionResult::PENDING,表示事件暂时pending
return InputEventInjectionResult::PENDING;
// 假如现在的时间比ANR发生的时间要晚,即在ANR发生的时间再次check,仍然是有焦点应用没有焦点窗口
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
// 那么就丢弃此次事件
ALOGE("Dropping %s event because there is no focused window",
NamedEnum::string(entry.type).c_str());
// 表示事件分发失败
return InputEventInjectionResult::FAILED;
} else {
// 如果没有达到ANR的时间,那么事件就继续pending
// Still waiting for the focused window
return InputEventInjectionResult::PENDING;
}
}
// we have a valid, non-null focused window
// 如果有一个有效的非空的焦点窗口,重置ANR超时
resetNoFocusedWindowTimeoutLocked();
// Check permissions. 检查注入者的权限,如果返回false,就证明没权限
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
return InputEventInjectionResult::PERMISSION_DENIED;
}
// 如果窗口此时处于paused状态,此时输出窗口的名称,并对事件进行pending
if (focusedWindowHandle->getInfo()->paused) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
return InputEventInjectionResult::PENDING;
}
// If the event is a key event, then we must wait for all previous events to
// complete before delivering it because previous events may have the
// side-effect of transferring focus to a different window and we want to
// ensure that the following keys are sent to the new window.
//
// Suppose the user touches a button in a window then immediately presses "A".
// If the button causes a pop-up window to appear then we want to ensure that
// the "A" key is delivered to the new pop-up window. This is because users
// often anticipate pending UI changes when typing on a keyboard.
// To obtain this behavior, we must serialize key events with respect to all
// prior input events.
// 确保在发送按键事件之前,所有先前的事件都已经处理完毕。因为旧的事件有可能可能会导致焦点转移到不同的窗口,而我们
// 希望确保后续的按键事件被发送到新窗口。
// 举个例子,如果用户在一个窗口中点击了一个按钮,然后立即按下键盘上的"A"键。如果点击按钮导致弹出一个新窗口,
// 我们希望确保"A"键被发送到新的弹出窗口。这是因为用户在键盘输入时通常会预期即将发生的UI变化。
// 为了实现这种行为,我们需要按键事件与先前的输入事件进行串行化处理
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
//事件需要继续等待,则设置超时时间给nextWakeupTime,这里超时事件即下次dispatch唤醒的时间
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
//事件继续pending
return InputEventInjectionResult::PENDING;
}
}
// 添加window信息封装为InputTarget,添加到InputTargets队列
// Success! Output targets.
addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
BitSet32(0), inputTargets);
// 返回SUCCEEDED,表明focuswindow存在
// Done.
return InputEventInjectionResult::SUCCEEDED;
}
resetNoFocusedWindowTimeoutLocked
/**
* 重置ANR的超时
*/
void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
if (DEBUG_FOCUS) {
ALOGD("Resetting ANR timeouts.");
}
// Reset input target wait timeout.
// ANR的时间设置为空
mNoFocusedWindowTimeoutTime = std::nullopt;
// 焦点应用信息重置
mAwaitedFocusedApplication.reset();
}
shouldWaitToSendKeyLocked
/**
* key事件是否需要等之前的输入事件处理完毕,返回true是需要等,返回false是不需要等
*/
bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
const char* focusedWindowName) {
// 如果mAnrTracker为空,代表已经处理了所有的等待事件
if (mAnrTracker.empty()) {
// already processed all events that we waited for
// 把mKeyIsWaitingForEventsTimeout的值赋值给空
mKeyIsWaitingForEventsTimeout = std::nullopt;
return false;
}
// 代码走到这里表示mAnrTracker不为空,即依然有事件需要处理,以下代码都是有这个前提条件的
// 假如mKeyIsWaitingForEventsTimeout为空,表示计时器并没有启动
if (!mKeyIsWaitingForEventsTimeout.has_value()) {
// Start the timer
// Wait to send key because there are unprocessed events that may cause focus to change
// 启动一个定时器,并将mKeyIsWaitingForEventsTimeout
// 设置为当前时间(currentTime)加上等待时间阈值(KEY_WAITING_FOR_EVENTS_TIMEOUT 500ms)
// 启动计时器的目的是等待一段时间,以确保在发送按键事件之前,所有可能干扰焦点的事件都得到了处理。
// 这样可以保证按键事件被正确地发送到适当的窗口
mKeyIsWaitingForEventsTimeout = currentTime +
std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT)
.count();
// 表示需要等待按键事件
return true;
}
// We still have pending events, and already started the timer
// key事件需要等够500ms才行
if (currentTime < *mKeyIsWaitingForEventsTimeout) {
return true; // Still waiting
}
// Waited too long, and some connection still hasn't processed all motions
// Just send the key to the focused window
// 如果等待的事件超过了500ms,依然存在其他未处理的事件,不再等待。mKeyIsWaitingForEventsTimeout设为空
ALOGW("Dispatching key to %s even though there are other unprocessed events",
focusedWindowName);
mKeyIsWaitingForEventsTimeout = std::nullopt;
return false;
}
addWindowTargetLocked
/*
#include <iostream>
#include <vector>
#include <algorithm>
bool isEven(int num) {
return num % 2 == 0;
}
int main() {
std::vector<int> numbers = {1, 3, 5, 7, 2, 4, 6, 8};
// 使用 std::find_if 查找第一个偶数
auto it = std::find_if(numbers.begin(), numbers.end(), isEven);
if (it != numbers.end()) {
std::cout << "找到了第一个偶数:" << *it << std::endl;
} else {
std::cout << "未找到满足条件的元素" << std::endl;
}
return 0;
}
*/
/**
* 添加window信息封装为InputTarget,添加到InputTargets队列
**/
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
//std::find_if用来查找满足参数3的元素,如果找到就返回该元素的迭代器,如果找不到就返回结束迭代器
//参数3的条件为窗口Token=目标所在渠道的ConnectionToken
std::vector<InputTarget>::iterator it =
std::find_if(inputTargets.begin(), inputTargets.end(),
[&windowHandle](const InputTarget& inputTarget) {
return inputTarget.inputChannel->getConnectionToken() ==
windowHandle->getToken();
});
//获取窗口信息
const InputWindowInfo* windowInfo = windowHandle->getInfo();
//如果没找到合适的inputTarget
if (it == inputTargets.end()) {
//则创建一个
InputTarget inputTarget;
//获取窗口所关联的InputChannel
std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(windowHandle->getToken());
//如果注销了input channel,则输出该窗口的名字,并直接返回
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
return;
}
//初始化inputTarget
inputTarget.inputChannel = inputChannel;
//如果该代码执行来自findFocusedWindowTargetsLocked
//flags值为InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS
inputTarget.flags = targetFlags;
//设置输入目标的全局缩放因子为窗口信息的全局缩放因子
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
//设置输入目标的显示尺寸为窗口信息的显示宽度和高度
inputTarget.displaySize =
int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
//将window信息封装到inputTarget,并存储到inputTargets的队列后面
inputTargets.push_back(inputTarget);
//将it设置为刚才初始化的inputTarget对象
it = inputTargets.end() - 1;
}
//除了校验token,还要校验flags和globalScaleFactor
ALOG_ASSERT(it->flags == targetFlags);
ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
//inputTarget的内容,我们以后再讲解,这句就不再说了
it->addPointers(pointerIds, windowInfo->transform);
}
checkInjectionPermission
/**
* 检查注入者的权限:参数1为窗口,参数2位注入事件的相关信息
* 返回false代表没有权限,返回true代表有权限
**/
bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState) {
//如果injectionState不为空,并且(窗口为空或者窗口的UID与注入事件的UID不匹配)并且注入事件的pid和uid经过校验没有权限
//那么此时会输出注入事件的pid和uid,如果窗口不为空,还会输出窗口的名称以及窗口所在UID
if (injectionState && (windowHandle == nullptr || windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
&& !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
if (windowHandle != nullptr) {
ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
"owned by uid %d",
injectionState->injectorPid, injectionState->injectorUid,
windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid);
} else {
ALOGW("Permission denied: injecting event from pid %d uid %d",
injectionState->injectorPid, injectionState->injectorUid);
}
return false;
}
return true;
}
看完findFocusedWindowTargetsLocked有助于分析dispatchKeyLocked和dispatchMotionLocked,也有助于分析dispatchOnce逻辑等等。