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

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统源码是按照功能进行分类的,主要分为系统代码、工具、文档、开发环境、虚拟机、配置脚本和编译脚本等类别。其中,系统代码是Android系统的核心部分,包含了各个功能模块的实现代码。工具包括了用于Android系统开发和调试的工具,例如adb、emulator等。文档部分包含了Android系统的开发文档和相关说明。开发环境是Android系统的开发所需的各种环境、库和工具。虚拟机是用于运行Android应用程序的Dalvik虚拟机。配置脚本和编译脚本是用于配置和编译Android系统的脚本文件。 Android系统采用的是一个从BSD继承而来的标准的系统函数库bionic。它是一个轻量级的C库,专门为Android系统进行了优化和定制。在源码根目录下有bionic文件夹,它包含了bionic库的源代码和相关文件。 Android4.3程序库的类型非常多,功能也非常强大。其中一些常用且重要的系统程序库包括: - libcore:Android系统的核心库,提供了Java核心类库的实现,包括集合、IO、网络等功能。 - libandroid_runtime:Android运行时库,提供了Android应用程序运行所需的功能,例如应用程序的启动和管理、进程间通信等。 - libui:Android系统的用户界面库,提供了绘制窗口、图形渲染等功能。 - libsqlite:SQLite数据库库,提供了数据库的管理和操作功能。 - libmedia:媒体库,提供了音频和视频的播放和录制功能。 以上是Android系统源码分析的一些基本信息。如果你有更具体的问题,可以告诉我,我会尽力帮助你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值