Android findFocusedWindowTargetsLocked源码分析

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逻辑等等。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容