InputManager(3)--键盘事件的分发[part 2:InputDispatcher 事件分发]

②. InputDispatcher 事件分发

  • 经过 part 1的分析,我们可以知晓InputDispatcher对象是如何获取到输入事件的,那么接下来就让我们来梳理一下,当 InputDispatcher 获取到输入事件后,是如何进行分发的

⑴. notifyKey()

android/frameworks/…/InputDispatcher.cpp

//入参 NotifyKeyArgs 对象中保存的是 键盘事件
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
    ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
          "policyFlags=0x%x, action=0x%x, "
          "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
          args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
          args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
          args->downTime);
#endif
    //判断当前按键动作是否是合法的,合法的即为down和up两种状态
    if (!validateKeyEvent(args->action)) {
        return;
    }
    //拿到当前按键的处理策略,如 POLICY_FLAG_WAKE(是否需要唤醒设备) 、POLICY_FLAG_DISABLE_KEY_REPEAT(是否是重复按键)等等
    uint32_t policyFlags = args->policyFlags;
    //
    int32_t flags = args->flags;
    //获取到是否是 组合键 
    int32_t metaState = args->metaState;
    //先将重复次数置为0
    constexpr int32_t repeatCount = 0;
    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
        policyFlags |= POLICY_FLAG_VIRTUAL;
        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
    }
    if (policyFlags & POLICY_FLAG_FUNCTION) {
        metaState |= AMETA_FUNCTION_ON;
    }

    policyFlags |= POLICY_FLAG_TRUSTED;

    //获取映射键值
    int32_t keyCode = args->keyCode;
    //根据是否是组合键,转换键值
    accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);

    //构建出一个 KeyEvent 对象,并使用从 InputReader 中传过来的 NotifyKeyArgs 对其进行初始化
    KeyEvent event;
    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
                     args->downTime, args->eventTime);

    android::base::Timer t;
    //调用 mPolicy 在按键事件入队列之前先进行处理,mPolicy 就是 NativeInputManager 对象
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    bool needWake;
    { // acquire lock
        mLock.lock();
        //判断是否需要对按键事件进行过滤,其实就是判断 mInputFilterEnabled 的值
        //mInputFilterEnabled默认为 false,该成员变量可由 setInputFilterEnabled() 函数进行修改
        if (shouldSendKeyToInputFilterLocked(args)) {
            //如果命中if,那么就说明需要判断按键是否需要过滤,那么先解锁
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            //借由 mPolicy 来判断当前按键事件是否需要过滤,如果需要过滤,那么就会命中if,那么此处直接return,不会在进行分发
            //之所以上面要进行解锁,也是因为如果命中if就会直接return
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }
            //如果当前按键事件无需过滤,那么再将锁加上
            mLock.lock();
        }
        //根据 InputReader 传过来的 NotifyKeyArgs 对象,构建出一个 KeyEntry 对象
        KeyEntry* newEntry =
                new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
                             args->displayId, policyFlags, args->action, flags, keyCode,
                             args->scanCode, metaState, repeatCount, args->downTime);
        //将新建的 KeyEntry 对象入队列,入队成功,就需要唤醒 InputDispatcher 线程的looper,进行分发
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock
    //如果入队成功,那么唤醒 InputDispatcher 线程的looper,开始分发按键事件
    if (needWake) {
        mLooper->wake();
    }
}
  • InputDispatcher 对象中的 notifyKey() 方法中,大致可分给三个处理步骤:

    1. 首先会进行 按键事件 合法性的判断,在判断合法之后,还会进行一些处理措施的转换,如判断是否是组合键,并执行针对键值的相应转化操作,主要由:accelerateMetaShortcuts() 函数完成
    2. 在完成一系列判断之后,会首先根据从 InputReader 对象传过来的 NotifyKeyArgs 对象构建一个 KeyEvent 对象,然后将该 KeyEvent 对象交给 mPolicy 在入队列之前进行处理,主要由:interceptKeyBeforeQueueing() 函数完成
    3. 判断是否设置了 按键过滤器,如果有则判断是否需要对该按键事件进行过滤,如果需要过滤,那么就会直接返;否则,就将该 键盘事件 转换成 KeyEntry 对象,并将其放入 InputDispatcher 的待处理队列中,插入成功后,唤醒 InputDispatcher 线程的 looper 循环,开始分发处理键盘事件,入队操作主要由:enqueueInboundEventLocked() 函数完成

    那么在解析入队和键盘事件分发之前,先来看看如何借由 interceptKeyBeforeQueueing() 函数进行键盘事件的初步处理,在 interceptKeyBeforeQueueing() 函数中又会做些什么动作。

1.0. interceptKeyBeforeQueueing()
  • 根据 InputManager(1)–InputManager的创建和启动 篇章的介绍,可以得知 InputDispatcher 对象中的 mPolicy 变量其实就是 NativeInputManager 对象,那么就会首先调用到 NativeInputManager::interceptKeyBeforeQueueing() 函数

android/frameworks/…/com_android_server_input_InputManagerService.cpp

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ATRACE_CALL();
    //处理策略:
    //1. 忽略不受信任的事件,并给他们打上POLICY_FLAG_PASS_TO_USER和POLICY_FLAG_INTERACTIVE标记
    //2. 询问窗口管理器如何处理正常事件和可信注入事件
    //3. 对于正常事件,如果当前关闭或变暗,则唤醒屏幕并使屏幕变亮
    bool interactive = mInteractive.load();
    if (interactive) {
        policyFlags |= POLICY_FLAG_INTERACTIVE;
    }
    //如果当前事件是被信任的,那么命中if
    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime();
        JNIEnv* env = jniEnv();
        //将C++层的KeyEvent对象转化成java的对象
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        //转换成功,会命中if
        if (keyEventObj) {
            //回调到mServiceObj也就是 InputManagerService.java 中的 interceptKeyBeforeQueueing() 函数
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            //判断是否调用成功
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            //清理处理完成的 KeyEventObj 对象
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }
        //根据wmActions的值判断该事件是否被处理,如果没有处理,那么将policyFlags加上POLICY_FLAG_PASS_TO_USER标志
        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
    } else {
        if (interactive) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;
        }
    }
}
  • NativeInputManager 对象中的 interceptKeyBeforeQueueing() 函数内,主要就是通过 JNI 反向调用回了 JAVA 层,通过 JAVA层的 InputManagerService 对象的 interceptKeyBeforeQueueing() 函数去进一步处理该事件
1.1. interceptKeyBeforeQueueing()

android/frameworks/…/InputManagerService.java

// Native callback.
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    //mWindowManagerCallbacks就是 InputManagerCallback 对象
    return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
  • InputManagerService 中的 mWindowManagerCallbacks 成员变量是通过 setWindowManagerCallbacks() 函数进行设置的,而这个函数是在 SystemService 进程中,创建和启动 InputManagerService 对象时同步进行设置的,且传入的参数是 WindowManagerService.getInputManagerCallback() 这部分具体的分析可翻阅 InputManager(1)–InputManager的创建和启动 篇章的 InputManagerService的启动 章节进行查看,查看 WindowManagerService.getInputManagerCallback() 函数最终返回的就是一个 InputManagerCallback 对象:public InputManagerCallback getInputManagerCallback() { return mInputManagerCallback; }
1.2. interceptKeyBeforeQueueing()

android/frameworks/…/InputManagerCallback.java

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    //mService.mPolicy就是 PhoneWindowManager 对象
    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
  • InputManagerCallback 对象中,mService 成员变量就是 WindowManagerService 对象,而 WMS 内部的 mPolicy 成员变量是在 WMS 创建的时候进行赋值的,而查看 SystemService 进程中创建 WMS 传入的参数:wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); 可见,WMS 中的 mPolicy 成员变量其实就是 PhoneWindowManager 对象
1.3. interceptKeyBeforeQueueing()

android/frameworks/…/PhoneWindowManager.java

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
 ...   
}
  • 最终调用到 PhoneWindowManager 中的 interceptKeyBeforeQueueing() 函数,在这里键盘事件会进行首次处理,在 PhoneWindowManager 对象中还保存有 PMSWMS 对象,在此处也会根据需要将键盘事件传递给这两个对象
2.0. enqueueInboundEventLocked()

android/frameworks/…/InputDispatcher.cpp

//该函数的入参是封装了键盘事件的 KeyEntry 对象;返回值为 needWake,表示是否需要将 InputDispatcher 线程进行唤醒
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    //如果当前 待分发事件队列 mInboundQueue 为空,那么需要立即唤醒 InputDispatcher 线程处理事件
    bool needWake = mInboundQueue.empty();
    //将 新来的 KeyEntry 对象放入 mInboundQueue 队列中
    mInboundQueue.push_back(entry);
    traceInboundQueueLengthLocked();
    //根据事件类型进行处理
    switch (entry->type) {
        //如果是按键
        case EventEntry::Type::KEY: {
            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
            //判断该按键事件是否是 app 切换事件,也就是是否涉及窗口变化事件
            if (isAppSwitchKeyEvent(keyEntry)) {
                //如果是按下状态,设置mAppSwitchSawKeyDown为true
                if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
                    mAppSwitchSawKeyDown = true;
                } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                    //当前是松开状态,且mAppSwitchSawKeyDown为true,设置返回值为 true
                    if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
                        ALOGD("App switch is pending!");
#endif
                        //设置事件响应超时时间 mAppSwitchDueTime,超时时间为 0.5 s
                        mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                        //复位 mAppSwitchSawKeyDown 标志
                        mAppSwitchSawKeyDown = false;
                        //设置返回值 needWake 为 true
                        needWake = true;
                    }
                }
            }
            break;
        }
	    ...
    }

    return needWake;
}
  • 在 入队的过程中,还会根据当前 队列的状态 和 当前入队的事件类型 进行判断,从而得出是否需要将 InputDispatcher 线程立即唤醒,因为 InputDispatcher 的工作流程是:每一次 InputDispatcher 线程分发 完一个事件,那么它会进入睡眠状态,进行等待,直到对应的应用程序窗口 返回数据通知 InputDispatcher 线程对应的事件处理完成,InputDispatcher 线程才会被再次唤醒,进而去处理下一个键盘事件;那么从该函数的分析中就可以看到,当当前事件为键盘事件时,有两种情况需要立即唤醒 InputDispatcher 线程:
    1. 当前 待分发队列 mInboundQueue 为空时,此时 InputDispatcher 是处于睡眠状态的,需要唤醒它,去处理新的键盘事件
    2. 当前键盘事件涉及窗口的切换时,为了提高响应速度,提升交互体验,需要在按键松开时立即唤醒 InputDispatcher 线程去分发按键,并且还会为该键盘事件设置一个超时时间—APP_SWITCH_TIMEOUT,默认是 0.5 s
  • 在完成了键盘事件入队之后,并且假设当前唤醒了 InputDispatcher 线程,那么此时 InputDispatcher 线程就会调用 dispatchOnce() 函数开始取出 待分发队列 mInboundQueue 中的事件开始进行分发处理

⑵. dispatchOnce()

android/frameworks/…/InputDispatcher.java

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        //如果当前mCommandQueue队列为空,那么就会命中if
        if (!haveCommandsLocked()) {
            //检查当前系统中是否有新的键盘事件需要分发
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        //如果当前mCommandQueue不为空,那么就需要执行
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
	...
}
  • 当前我们先假设 mCommandQueue 队列为空,也就是会调用到 dispatchOnceInnerLocked() 函数,那么接下来我们看一下具体流程
1.0. dispatchOnceInnerLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    //先拿到当前的时间
    nsecs_t currentTime = now();

    //如果命中if,那么就需要复位重复按键状态
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }

    //如果当前分发线程被冻结,那么不再进行事件分发,直接返回
    if (mDispatchFrozen) {
        if (DEBUG_FOCUS) {
            ALOGD("Dispatch frozen.  Waiting some more.");
        }
        return;
    }

    //判断设置的涉及窗口切换的事件的超时事件是否小于当前时间,即判断是否已经超过处理超时时间了,将比较结果保存到 isAppSwitchDue 中
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    //判断下一次唤醒事件和窗口切换的事件的超时事件的大小,如果 超时事件更早,那么将下次唤醒事件设置为 mAppSwitchDueTime
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    //先判断上一个需要分发的事件是否已经完成分发了,如果 mPendingEvent 不为null,说明还未完成分发
    //mPendingEvent==null,会命中if
    if (!mPendingEvent) {
        //判断待分发队列 mInboundQueue 是否为空,命中if代表当前待分发队列 mInboundQueue 为空
        if (mInboundQueue.empty()) {
            //如果涉及窗口切换的事件的超时时间已经过了,命中if
            if (isAppSwitchDue) {
                //将mAppSwitchDueTime设置为最大值,因为此时待分发事件队列 mInboundQueue 为空
                //即不会再有按键需要分发了,所以复位这个超时事件
                resetPendingAppSwitchLocked(false);
                //将比较结果置为 false
                isAppSwitchDue = false;
            }

            //判断当前是否存在重复按键事件,存在则命中if
            if (mKeyRepeatState.lastKeyEntry) {
                //判断当前事件是否已经到了发送重复按键的时间了
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    //如果进入if,即已到达分发事件,那么就合成一个 键盘事件将他保存到 
                    mPendingEvent 变量中
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    //进入else,则代表还没到重复按键下一次分发事件,那么就将比较下一次唤醒时间和下一次重复按键分发时间的大小
                    //将时间更早的保存到 nextWakeupTime 中,作为下一次唤醒时间
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            //如果不存在待分发事件,那么直接返回
            if (!mPendingEvent) {
                return;
            }
        } else {
            //进入这个else,代表当前待分发队列 mInboundQueue 不为空,那么取出队列头对象,保存在mPendingEvent中
            mPendingEvent = mInboundQueue.front();
            //将已被取出的元素出队列
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }
	    //判断当前事件是否需要继续分发
        //命中if,则调用 pokeUserActivityLocked() 将当前键盘事件分发到java层的 PowerManagerService 中
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(*mPendingEvent);
        }
    }

    ALOG_ASSERT(mPendingEvent != nullptr);
    bool done = false;
    //初始化 键盘事件 被丢弃 原因
    DropReason dropReason = DropReason::NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DropReason::POLICY;
    } else if (!mDispatchEnabled) {
        dropReason = DropReason::DISABLED;
    }

    //如果下一个不阻塞的事件==当前需要处理的事件,那么设置mNextUnblockedEvent为空
    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = nullptr;
    }
    //根据事件类型进行分别处理
    switch (mPendingEvent->type) {
        ...
        case EventEntry::Type::KEY: {
            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
            //判断涉及窗口切换的事件的超时时间是不是已经到了,命中if则表明还未到超时时间
            if (isAppSwitchDue) {
                //判断是否是切换窗口的按键事件
                if (isAppSwitchKeyEvent(*typedEntry)) {
                    //设置 mAppSwitchDueTime 为最大值,此时开始处理这个窗口切换键盘事件了
                    resetPendingAppSwitchLocked(true);
                    //将标志置为 false,因为此时已经开始处理这个键盘事件了
                    isAppSwitchDue = false;
                } else if (dropReason == DropReason::NOT_DROPPED) {
                    //需要处理窗口切换事件,需要丢弃其他的普通键盘事件,所以设置dropReason为APP_SWITCH
                    dropReason = DropReason::APP_SWITCH;
                }
            }
            //判断当前键盘事件并未被删除,且还未过 键盘事件的超时时间,默认的普通按键的超时时间为 STALE_EVENT_TIMEOUT(10s)
            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
                dropReason = DropReason::STALE;
            }
            //判断当前键盘事件并未被删除,且mNextUnblockedEvent不为空,那么需要设置其为 blocked 
            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                dropReason = DropReason::BLOCKED;
            }
            //调用 dispatchKeyLocked() 函数分发,返回值为是否完成分发,决定是否需要将事件从mPendingEvent中删除
            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
	    ...
    }

    if (done) {
        if (dropReason != DropReason::NOT_DROPPED) {
            //处理完成,将事件删除
            dropInboundEventLocked(*mPendingEvent, dropReason);
        }
        //记录下当前事件处理删除的理由,此时的当前事件对应下一个即将处理的事件就是前一个事件
        mLastDropReason = dropReason;
	    //释放空间
        releasePendingEventLocked();
        //将nextWakeupTime设置为无限小,即立即唤醒 InputDispatcher 线程,开始下一轮循环
        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
    }
}
  • dispatchOnceInnerLocked() 函数中,完成的事情比较多,大体总结一下在该函数中都做了哪些事情:

    1. mPendingEvent 成员变量存放的就是正在处理中的事件,如果该变量为 null,那么说明上一个事件已经处理完成,可以开始获取下一个待处理的事件进行处理了;在从待分发事件队列 mInboundQueue 中获取事件时,会存在两种情况:

      1. mInboundQueue 队列为空,判断窗口切换操作相关的标志位还为 true ,那么就会直接将相关标志复位,因为此时已经不存在待处理的事件了;接下来判断是否存在重复按键事件,如果有且判断已经到了下一次分发按键的事件,那么就会合成一个按键事件并将他保存到 mPendingEvent 变量中;而如果还没到下一次分发时间 ,那么就比较下一次分发时间 mKeyRepeatState.nextRepeatTime 和下一次唤醒时间 nextWakeupTime,将需要更早响应的时间存放到 nextWakeupTime 变量中;而如果结束上面的动作后,mPendingEvent 变量仍为空,那么说明此时没有事件需要处理,就直接返回
      2. mInboundQueue 队列不为空,那么直接取出队首的事件元素,并将对应元素从 mInboundQueue 队列中删除

      经过上面的两步,此时 mPendingEvent 中肯定不为空,那么在判断 事件标志有 POLICY_FLAG_PASS_TO_USER 时,调用 pokeUserActivityLocked() 函数将输入事件先转发到 JAVA层的 PowerManagerService 对象中

    2. 接下来按照 输入事件类型来区分处理,此处我们假设输入事件为键盘事件,在判断还未到 窗口切换的输入事件的处理超时时间之前,又有两种处理方式:

      1. 当前要处理的输入事件就是切换窗口相关的输入事件,那么复位相关窗口切换事件的标志位,此时复位是因为接下来就会处理该事件,所以可以将这些标志进行复位
      2. 当前要处理的输入事件不是切换窗口相关的输入事件,那么此时设置 dropReason = DropReason::APP_SWITCH ,那么在接下来的处理中,就会将切换窗口之外的输入事件都进行丢弃,保证优先处理窗口切换相关的输入事件

      之后调用 dispatchKeyLocked() 进行输入事件的进一步处理

    3. 在处理完成后,将输入事件删除,并且释放空间,最后还会使用变量 mLastDropReason 记录下这个处理完成的输入事件的删除理由,设置下一次唤醒时间为无限小,表示需要马上唤醒 InputDispatcher 线程 looper 循环,进行下一个输入事件的分发

  • 按照这三个大步骤,我们主要来梳理一下 pokeUserActivityLocked()dispatchKeyLocked() 函数流程

1.0.0. pokeUserActivityLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
    //如果输入事件类型为 FOCUS,那么代表只将事件传给应用程序窗口,不代表是用户操作,不需要触发相关操作,直接return
    if (eventEntry.type == EventEntry::Type::FOCUS) {
        return;
    }
    //拿到输入事件的目标窗口 displayId
    int32_t displayId = getTargetDisplayId(eventEntry);
    //获取到对应的 InputWindowHandle 对象
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    if (focusedWindowHandle != nullptr) {
        const InputWindowInfo* info = focusedWindowHandle->getInfo();
        //如果该窗口是禁止输入的,那么同样不分发事件,直接return
        if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
#if DEBUG_DISPATCH_CYCLE
            ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
#endif
            return;
        }
    }

    int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
    switch (eventEntry.type) {
        ...
        case EventEntry::Type::KEY: {
            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
            if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
                return;
            }
            //设置当前的eventType为USER_ACTIVITY_EVENT_BUTTON
            eventType = USER_ACTIVITY_EVENT_BUTTON;
            break;
        }
        ...
    }
    //构建一个 CommandEntry 对象,传入的 Command 为 doPokeUserActivityLockedInterruptible() 函数指针
    std::unique_ptr<CommandEntry> commandEntry =
            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
    commandEntry->eventTime = eventEntry.eventTime;
    commandEntry->userActivityEventType = eventType;
    //将该 CommandEntry 对象放入 mCommandQueue 队列中
    postCommandLocked(std::move(commandEntry));
}

void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
    //将 CommandEntry 对象放入了 mCommandQueue 队列中
    mCommandQueue.push_back(std::move(commandEntry));
}
  • 判断当前事件是用户活动,那么需要进行执行相关的操作,那么就会构建出一个 CommandEntry 对象,其中的 Command 变量是 doPokeUserActivityLockedInterruptible() 函数指针,且存入了当前事件的 eventTimeeventType,最后将该 CommandEntry 对象放入了 mCommandQueue 队列中
    • 在这里我们发现,其实在 该函数中,并没有直接处理输入事件,而是将其转换成了 CommandEntry 对象存入了 mCommandQueue 队列中
    • 并且我们也可以看到,在 pokeUserActivityLocked() 函数中构造的 CommandEntry 对象并没有保存对应的窗口的 InputChannel ,这其实就代表着,在 pokeUserActivityLocked() 函数中构筑的 CommandEntry 对象不是发给对应窗口的,具体是发给谁,又是怎么发送的我们放到后续 runCommandsLockedInterruptible() 函数中去讲
1.0.1. dispatchKeyLocked()

android/frameworks/…/InputDispatcher.cpp

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    //命中这个if,会先去判断是否是重复按键
    if (!entry->dispatchInProgress) {
        //当前按键重复次数为0,且处于按下状态,重复事件不被禁止且按键是被信任的,那么命中if
        if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
            (entry->policyFlags & POLICY_FLAG_TRUSTED) &&
            (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
            //当前新来的按键事件,就是上一次的按键,那么命中if,说明是长按事件
            if (mKeyRepeatState.lastKeyEntry &&
                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                //重复事件次数+1
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                //重置mKeyRepeatState
                resetKeyRepeatLocked();
                //此时不需要我们自己去产生重复按键,所以设置下一次合成重复按键的事件为最大值
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
            } else {
                //如果并不是重复按键
                //那么直接重置 mKeyRepeatState.lastKeyEntry
                resetKeyRepeatLocked();
                //设置下一次合成重复按键的时间
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
            }
            //将当前事件记录到 mKeyRepeatState 中
            mKeyRepeatState.lastKeyEntry = entry;
            //entry事件引用计数+1
            entry->refCount += 1;
        } else if (!entry->syntheticRepeat) {
            //当前事件并不是合成的重复事件,那么直接复位 mKeyRepeatState
            resetKeyRepeatLocked();
        }
        //如果 entry 的重复次数为1,说明是持续按下的状态,否则则不是重复按下的状态
        if (entry->repeatCount == 1) {
            entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
        } else {
            entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
        }
	   
        //设置dispatchInProgress为true,那么该 entry 对象如果尝试延迟分发,就不会重复进入判断
        entry->dispatchInProgress = true;

        logOutboundKeyDetails("dispatchKey - ", *entry);
    }

    //如果当前的事件是上一次的要求稍后再进行尝试分发的事件,那么命中if
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        //判断是否已经到达了之前要求的延迟分发的事件,如果没到,且下次唤醒事件晚于延迟时间,
        //那么将延迟唤醒分发的时间设置到nextWakeupTime中,然后直接返回false,等待下一次唤醒再处理该事件
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        //没有返回说明已经到了处理时间,那么复位延迟的相关标志
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    //如果当前事件的拦截结果未知,命中if,说明需要进行判断如何进行事件的拦截
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        //如果当前事件需要继续分发,那么命中if
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            //构建出一个 CommandEntry 对象,其中的 Command 对象是 doInterceptKeyBeforeDispatchingLockedInterruptible() 函数
            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            //拿到当前获得焦点的窗口
            sp<InputWindowHandle> focusedWindowHandle =
                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
            if (focusedWindowHandle != nullptr) {
                //如果当前有窗口获取焦点,那么获取该窗口的InputChannel,保存到 commandEntry 对象中
                commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
            }
            commandEntry->keyEntry = entry;
            //将新构建的 commandEntry 对象存放到 mCommandQueue 队列中等待处理
            postCommandLocked(std::move(commandEntry));
            //事件引用计数+1
            entry->refCount += 1;
            //返回false,因为当前事件还没有被处理,还不能将其删除
            return false; // wait for the command to run
        } else {
            //如果policyFlags不为POLICY_FLAG_PASS_TO_USER,说明需要继续进行拦截,设置对应标志位的值
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    //如果当前事件不需要进行拦截,那么命中该else if
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        //判断当前事件未被删除,那么设置删除理由为 POLICY
        if (*dropReason == DropReason::NOT_DROPPED) {
            *dropReason = DropReason::POLICY;
        }
    }

    // Clean up if dropping the event.
    //如果当前的事件不是未删除的,那么就将该事件清除掉,直接返回true
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(entry,
                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                             : INPUT_EVENT_INJECTION_FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;
    }

    // Identify targets.
    std::vector<InputTarget> inputTargets;
    //寻找合法的获取焦点的窗口
    int32_t injectionResult =
            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    //如果对应获取焦点的窗口还有输入事件待处理,那么此处先返回,等一会儿再来分发
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    //将注入结果,也就是可否分发事件到对应获取焦点窗口的结果保存到 entry 中
    setInjectionResult(entry, injectionResult);
    //如果没有注入成功,那么返回 true,删除这个事件
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));

    //分发按键
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
  • 经过梳理,我们可以看到 在 dispatchKeyLocked() 函数中是存在比较复杂的逻辑的,总结一下该函数中主要完成的工作:

    1. 首先会对 entry 事件进行判断,判断是否是重复事件,如果是重复按键,那么会将该 entry 对象存放到 mKeyRepeatState.lastKeyEntry 变量中

    2. 完成重复事件的判断后,还会判断当前的 entry 是否是需要延迟处理的,如果是,并且还没到延迟处理的事件,那么重置下一次唤醒时间后,就会返回,此时返回的是 false ,代表当前事件还未完成处理,不能被删除

    3. 继续往下就会来判断当前 entry 事件是否需要进行拦截了,如果需要拦截那么会根据处理策略进行分别处理:

      1. POLICY_FLAG_PASS_TO_USER : 构建一个 Command 对象为 doInterceptKeyBeforeDispatchingLockedInterruptible() 函数的 CommandEntry 对象,将其放入 mCommandQueue 队列后,就返回 false,等待该事件被处理,因为此时还未处理,所以不能将事件删除
      2. 其他:设置拦截处理结果为 INTERCEPT_KEY_RESULT_CONTINUE

      如果不跳过拦截处理,那么会设置丢弃理由,后续判断就会直接返回 true,删除该事件

    4. 再然后会借助 findFocusedWindowTargetsLocked() 函数查找当前获取焦点的窗口是否合法,并返回查找结果,根据返回结果来处理 entry 事件:

      1. INPUT_EVENT_INJECTION_PENDING:代表当前获取焦点的窗口还有其他事件需要处理,那么 dispatchKeyLocked() 函数直接返回 false,代表延迟一会儿再来处理该事件的分发,这是因为其他事件的处理有可能会引起获取焦点窗口的变化,而该事件是需要分发到获取焦点的窗口上的,所以需要等其他事件处理完成后,获取焦点的窗口是确定时,在进行分发处理
      2. !INPUT_EVENT_INJECTION_SUCCEEDED :代表无法拿到获取焦点的窗口,那么dispatchKeyLocked() 直接返回 true ,删除 entry 事件,不进行分发
    5. 最后,调用 dispatchEventLocked() 进行事件的分发

  • 根据以上的总结,我们在返回继续梳理 dispatchOnce() 函数之前,先来简略看一下 findFocusedWindowTargetsLocked() dispatchEventLocked() 完成的工作

1.0.1.0. findFocusedWindowTargetsLocked()

android/frameworks/…/InputDispatcher.cpp

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
                                                        const EventEntry& entry,
                                                        std::vector<InputTarget>& inputTargets,
                                                        nsecs_t* nextWakeupTime) {
    std::string reason;
    //根据 entry 事件获取到目标窗口的 displayId
    int32_t displayId = getTargetDisplayId(entry);
    //从 mFocusedWindowHandlesByDisplay 这个map里面去根据 displayId 获取到对应的 InputWindowHandle
    //也就是拿到当前获取焦点的窗口
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    //从 mFocusedApplicationHandlesByDisplay 这个map里面去根据 displayId 获取到对应的 InputApplicationHandle
    //也就是拿到当前获取焦点的应用
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);

    //如果无法根据目标窗口的 displayId 获取到目标窗口和目标应用,说明按键分发目标不存在,那么直接丢弃按键
    if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
        ALOGI("Dropping %s event because there is no focused window or focused application in "
              "display %" PRId32 ".",
              EventEntry::typeToString(entry.type), displayId);
        return INPUT_EVENT_INJECTION_FAILED;
    }

    //如果当前不存在获取焦点的窗口,但是存在获取焦点的应用,则命中if去判断是否存在 ANR
    if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
        //如果 mNoFocusedWindowTimeoutTime 不存在值,说明该分发事件刚发现没有焦点窗口,需要开始ANR的时间计数
        if (!mNoFocusedWindowTimeoutTime.has_value()) {
            //开始ANR计数,默认按键ANR时间为5S
            const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
                    DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
            //设置无响应时间为 currentTime+timeout
            mNoFocusedWindowTimeoutTime = currentTime + timeout;
            //将当前获取到焦点的应用保存到 mAwaitedFocusedApplication 变量中
            mAwaitedFocusedApplication = focusedApplicationHandle;
            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(), ns2ms(timeout));
            //设置下一次唤醒时间为 ANR 超时时间
            *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
            //返回 等待 标志
            return INPUT_EVENT_INJECTION_PENDING;
        //如果当前系统时间已经超过ANR超时时间,那么直接返回失败
        } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
            // Already raised ANR. Drop the event
            ALOGE("Dropping %s event because there is no focused window",
                  EventEntry::typeToString(entry.type));
            return INPUT_EVENT_INJECTION_FAILED;
        } else {
            //走入这个else,说明还未到ANR超时时间,返回继续等待
            return INPUT_EVENT_INJECTION_PENDING;
        }
    }

    //走到这里说明,有获取焦点的窗口,那么复位 相关 NoFocusedWindow 的标志
    resetNoFocusedWindowTimeoutLocked();

    //核对分发按键的权限
    if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
        return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
    }
    //如果当前获取焦点的窗口处于 paused 状态,那么返回继续等待
    if (focusedWindowHandle->getInfo()->paused) {
        ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
        return INPUT_EVENT_INJECTION_PENDING;
    }

    //如果当前输入事件是键盘事件,那么需要等待之前的事件处理完成后,在进行当前事件的分发
    if (entry.type == EventEntry::Type::KEY) {
        //判断是否需要等待之前事件分发完成,返回true,代表需要继续等待
        if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
            //设置下次唤醒时间为 等待之前事件处理时间
            *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
            return INPUT_EVENT_INJECTION_PENDING;
        }
    }

    //可以正确分发事件,将目标窗口相关的参数添加到 inputTargets 容器中
    addWindowTargetLocked(focusedWindowHandle,
                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
                          BitSet32(0), inputTargets);

    // Done.
    return INPUT_EVENT_INJECTION_SUCCEEDED;
}
  • 从该函数的分析中可以看到,主要就是在查找 获取焦点的目标分发窗口,且会判断当前窗口的状态,以及是否还有其他事件需要处理,从而返回针对当前输入事件的处理措施,在这个函数流程中,其中有两个函数需要再展开看一下:

    1. shouldWaitToSendKeyLocked()

      bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
                                                      const char* focusedWindowName) {
          //如果 mAnrTracker 为空,那么说明已经没有其他事件需要处理了
          if (mAnrTracker.empty()) {
              //将mKeyIsWaitingForEventsTimeout置空后直接返回false,直接分发该事件给当前焦点窗口
              mKeyIsWaitingForEventsTimeout = std::nullopt;
              return false;
          }
      
          //如果mAnrTracker不为空,但是mKeyIsWaitingForEventsTimeout是空的,那么需要开启超时等待
          if (!mKeyIsWaitingForEventsTimeout.has_value()) {
              ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
                    "focus to change",
                    focusedWindowName);
              //设置 mKeyIsWaitingForEventsTimeout 超时5s
              mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
              //返回 true,等待处理之前的事件
              return true;
          }
      
          //如果还没到超时时间,那么返回 true,继续等待处理之前的事件
          if (currentTime < *mKeyIsWaitingForEventsTimeout) {
              return true; // Still waiting
          }
          
          ALOGW("Dispatching key to %s even though there are other unprocessed events",
                focusedWindowName);
          //走到这里表示,已经过了该事件的超时等待时间,返回false,直接将该事件分发给当前窗口
          mKeyIsWaitingForEventsTimeout = std::nullopt;
          return false;
      }
      
      • 可以看到,在该函数中主要就是针对是否存在之前的键盘事件,已经当前按键事件等待的时间来判断是否需要继续等待,或者直接分发;而之所以要进行这一步操作,原因是:针对键盘事件,我们需要将其分发到最新的获取焦点的窗口上,而又因为键盘事件可能导致窗口的变化,所以最好是等待之前的键盘事件都处理完成后,再进行当前的键盘事件的处理
    2. addWindowTargetLocked()

      void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
                                                  int32_t targetFlags, BitSet32 pointerIds,
                                                  std::vector<InputTarget>& inputTargets) {
          //先从传入的 inputTargets 容器中查找,是否已经存在了 windowHandle 所代表的 Inputchannel                                            
          std::vector<InputTarget>::iterator it =
                  std::find_if(inputTargets.begin(), inputTargets.end(),
                               [&windowHandle](const InputTarget& inputTarget) {
                                   return inputTarget.inputChannel->getConnectionToken() ==
                                           windowHandle->getToken();
                               });
      
          //从 windowHandle 中拿到 InputWindowInfo 对象
          const InputWindowInfo* windowInfo = windowHandle->getInfo();
      
          //命中if,代表 inputTargets 容器中不存在 windowHandle 所代表的 InputChannel
          if (it == inputTargets.end()) {
              InputTarget inputTarget;
              //从 windowHandle 中获取到对应窗口的 InputChannel
              sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
              if (inputChannel == nullptr) {
                  ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
                  return;
              }
              //将相关数据封装到 InputTarget 对象中
              inputTarget.inputChannel = inputChannel;
              inputTarget.flags = targetFlags;
              inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
              //并将这个 InputTarget 对象放入 InputTargets 容器中
              inputTargets.push_back(inputTarget);
              it = inputTargets.end() - 1;
          }
      
          ALOG_ASSERT(it->flags == targetFlags);
          ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
      
          it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
                          windowInfo->windowXScale, windowInfo->windowYScale);
      }
      
      • 可以看到,在 inputTargets 容器中存放的就是我们需要分发到的窗口,其中记录着该窗口所具备的 InputChannel 对象
1.0.1.1. dispatchEventLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
    ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif

    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
    //调用 pokeUserActivityLocked() 将当前键盘事件分发到java层的 PowerManagerService 中
    pokeUserActivityLocked(*eventEntry);

    //遍历需要分发的窗口
    for (const InputTarget& inputTarget : inputTargets) {
        //根据 InputTargets 容器中的元素的 InputChannel 去获取 connection 对象
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            //如果获取到的 connection 对象不为空,那么开始分发该按键
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        } else {
            if (DEBUG_FOCUS) {
                ALOGD("Dropping event delivery to target with channel '%s' because it "
                      "is no longer registered with the input dispatcher.",
                      inputTarget.inputChannel->getName().c_str());
            }
        }
    }
}
  • 可以看到在该函数中,首先会将一个封装了doPokeUserActivityLockedInterruptible() 函数指针的 CommandEntry 对象,放入了 mCommandQueue 队列中等待执行,然后遍历需要分发的窗口列表 InputTargets ,保证获取到对应窗口的 connection 对象的前提下,调用 prepareDispatchCycleLocked() 开始准备分发输入事件给对应窗口
1.0.1.1.0 prepareDispatchCycleLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection,
                                                 EventEntry* eventEntry,
                                                 const InputTarget& inputTarget) {
...

    //判断 连接状态,不正常则不进行分发,直接return
    if (connection->status != Connection::STATUS_NORMAL) {
#if DEBUG_DISPATCH_CYCLE
        ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
              connection->getInputChannelName().c_str(), connection->getStatusLabel());
#endif
        return;
    }

    //判断目标窗口是否处于分屏状态,如果是分屏状态,需要进行一些额外的处理
    if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
        LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
                            "Entry type %s should not have FLAG_SPLIT",
                            EventEntry::typeToString(eventEntry->type));

        const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
        if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
            MotionEntry* splitMotionEntry =
                    splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
            if (!splitMotionEntry) {
                return; // split event was dropped
            }
            if (DEBUG_FOCUS) {
                ALOGD("channel '%s' ~ Split motion event.",
                      connection->getInputChannelName().c_str());
                logOutboundMotionDetails("  ", *splitMotionEntry);
            }
            //处理完成后,最后调用 该函数进行处理
            enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
            splitMotionEntry->release();
            return;
        }
    }

    //如果不是分屏状态,最后也是调用 enqueue函数进行处理
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
  • 可以看到,在该函数中,主要就是进行了判断当前窗口是否处在分屏状态,如果处于分屏状态,则需要进行一些额外的操作,但本篇关注的是键盘分发的流程,暂时先不进行分屏状态输入事件处理的细节分析,我们直接继续看不论是否分屏都会调用的 enqueueDispatchEntriesLocked() 处理函数
1.0.1.1.1. enqueueDispatchEntriesLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   EventEntry* eventEntry,
                                                   const InputTarget& inputTarget) {
...

    //记录目标窗口的 outboundQueue 队列是否为空
    bool wasEmpty = connection->outboundQueue.empty();

    //这里是为了将 KeyEntry 对象转成 DispatchEntry 对象,并存入 目标窗口 connection 的 outboundQueue 队列中
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

    //如果当前目标窗口的 outboundQueue 队列在执行enqueueDispatchEntryLocked()为空,执行后不为空,则命中if,继续向下执行
    if (wasEmpty && !connection->outboundQueue.empty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}
  • 在该函数中,主要就是将传入的 eventEntry 对象转换成 DispatchEntry 对象,并将其存入对应窗口 Connection 对象的 outboundQueue 队列中,然后进行发布
1.0.1.1.2 enqueueDispatchEntryLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                 EventEntry* eventEntry,
                                                 const InputTarget& inputTarget,
                                                 int32_t dispatchMode) {
...
    int32_t inputTargetFlags = inputTarget.flags;
    //异常情况,直接返回
    if (!(inputTargetFlags & dispatchMode)) {
        return;
    }
    inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;

    //构建一个 DispatchEntry 对象
    std::unique_ptr<DispatchEntry> dispatchEntry =
            createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);

    // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
    // different EventEntry than what was passed in.
    EventEntry* newEntry = dispatchEntry->eventEntry;
    // Apply target flags and update the connection's input state.
    switch (newEntry->type) {
        case EventEntry::Type::KEY: {
            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
            dispatchEntry->resolvedEventId = keyEntry.id;
            dispatchEntry->resolvedAction = keyEntry.action;
            dispatchEntry->resolvedFlags = keyEntry.flags;

            if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                 dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
                ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
                      connection->getInputChannelName().c_str());
#endif
                return; // skip the inconsistent event
            }
            break;
        }

        ...
    }

    // Remember that we are waiting for this dispatch to complete.
    if (dispatchEntry->hasForegroundTarget()) {
        incrementPendingForegroundDispatches(newEntry);
    }

    //将 dispatchEntry 对象添加到 outboundQueue 队列中,然后将其释放
    connection->outboundQueue.push_back(dispatchEntry.release());
    traceOutboundQueueLength(connection);
}
  • 在该函数中主要就是将 EntryKey 对象转换成 DispatchEntry 对象,然后放入 outboundQueue 对象中
1.0.1.1.3 startDispatchCycleLocked()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
...
    //循环的条件是 窗口连接通道状态正常,且当前 connection 中的 outboundQueue 队列不为空,即有待分发的事件
    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
        //拿出outboundQueue队列中的事件
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        dispatchEntry->deliveryTime = currentTime;
        //拿到分发超时时间
        const nsecs_t timeout =
                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
        //算出超时时刻,记录到 dispatchEntry 中
        dispatchEntry->timeoutTime = currentTime + timeout;

        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
            case EventEntry::Type::KEY: {
                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);

                //开始发布按键事件
                status =
                        connection->inputPublisher
                                .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
                                                 keyEntry->deviceId, keyEntry->source,
                                                 keyEntry->displayId, std::move(hmac),
                                                 dispatchEntry->resolvedAction,
                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                 keyEntry->scanCode, keyEntry->metaState,
                                                 keyEntry->repeatCount, keyEntry->downTime,
                                                 keyEntry->eventTime);
                break;
            }
		   ...
        }

        //publish 不成功,会命中if,判断完后会直接退出
        if (status) {
            //如果当前的发布状态是阻塞的
            if (status == WOULD_BLOCK) {
                //在阻塞的前提下,connection 对象的 等待队列为空,那么此时显然是异常状态,将 waitQueue和outboundQueue清空
                if (connection->waitQueue.empty()) {
                    ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                          "This is unexpected because the wait queue is empty, so the pipe "
                          "should be empty and we shouldn't have any problems writing an "
                          "event to it, status=%d",
                          connection->getInputChannelName().c_str(), status);
                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                } else {
                    //如果此时是通道满了,那么需要继续等待一会儿,不用清空waitQueue和outboundQueue队列
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
                          "waiting for the application to catch up",
                          connection->getInputChannelName().c_str());
#endif
                }
            } else {
                //发生错误,清空waitQueue和outboundQueue队列
                ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
                      "status=%d",
                      connection->getInputChannelName().c_str(), status);
                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
            }
            return;
        }

        //从outboundQueue中遍历dispatchEntry事件,然后将其从 outboundQueue 队列中删除
        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                                    connection->outboundQueue.end(),
                                                    dispatchEntry));
        traceOutboundQueueLength(connection);
        //将dispatchEntry事件放入waitQueue队列
        connection->waitQueue.push_back(dispatchEntry);
        //如果当前通道是能够响应的
        if (connection->responsive) {
            //那么将当前事件的超时时刻和对应inputchannel的本地binder对象(标识是在向哪个窗口发送消息)添加到 mAnrTracker 中,表示当前有事件在处理
            mAnrTracker.insert(dispatchEntry->timeoutTime,
                               connection->inputChannel->getConnectionToken());
        }
        traceWaitQueueLength(connection);
    }
}
  • 在该函数中,会遍历 outboundQueue 队列,拿出每一个队列中的每一个元素进行发布,在分发按键之前,会先判断事件的类型,根据不同的类型进行不同的发布方式,我们当前关注输入事件为键盘事件的场景,在调用 publishKeyEvent() 函数发布按键之后,会返回发布结果,而如果发布不成功,只有一种情况会不将 outboundQueue 队列中的数据清空,就是 connection->waitQueue 队列不为空的情况,此时代表着 传输通道已满,需要等待一会儿再进行新事件的发布;而在成功发布键盘事件后,会将该事件存放到 waitQueue 队列中,表示该输入事件正在等待答复,最后将该事件的 超时时刻 以及 代表 分发窗口的 本地 binder 对象放入 mAnrTracker 当中,至此我们就知道 在前面 1.0.1.0. 步骤中的 shouldWaitToSendKeyLocked() 函数中判断当前是否有事件正在处理的 mAnrTracker 数据的由来了。
1.0.1.1.4 publishKeyEvent()

android/framworks/…/InputTransport.cpp

status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                         int32_t source, int32_t displayId,
                                         std::array<uint8_t, 32> hmac, int32_t action,
                                         int32_t flags, int32_t keyCode, int32_t scanCode,
                                         int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                         nsecs_t eventTime) {
...

    if (!seq) {
        ALOGE("Attempted to publish a key event with sequence number 0.");
        return BAD_VALUE;
    }

    //构建一个 InputMessage 对象,将相关数据都封装到 InputMessage.body 和 msg.header中
    InputMessage msg;
    msg.header.type = InputMessage::Type::KEY;
    msg.body.key.seq = seq;
    msg.body.key.eventId = eventId;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.displayId = displayId;
    msg.body.key.hmac = std::move(hmac);
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    //通过 mChannel 也就是 InputChannel 对象,将消息发送出去
    return mChannel->sendMessage(&msg);
}
  • 在该函数中,就是将 键盘事件 封装到了 InputMessage 对象中,然后调用 mChannel 对象将其发送出去,而这个 mChannel 就是之前 注册到 InputManagerService 中的 server 端的 InputChannel
1.0.1.1.5 sendMessage()

android/frameworks/…/InputTransport.cpp

status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    //将传过来的 InputMessage 信息,拷贝到 cleanMsg 临时变量中
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        //将cleanMsg写入了 InputChannel 中去
        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    ...
    return OK;
}
  • 可以看到,最终是通过 send() 函数将 该输入消息 写入了 server 端的 InputChannel 中,而根据我们上一篇 InputManager(2)–InputChannel的注册 中的流程梳理我们知道,InputChannel 内部的 fd 变量其实就是 Socket 对象,那么此时其实我们就是向该 Socket 端进行数据写入, 而当我们在 server 端的 InputChannel 中写入数据,在 Client 端的 InputChannel 中的 Socket 就可以收到消息,然后就会开始调用 NativeInputEventReceiver 对象中的 handleEvent() 函数进行处理,这部分的详细分析可以见 InputChannel的注册篇章 。而至此,InputManager 端就将事件分发出去了,该按键事件此时就借由 InputChannel 中的 Socket 走到了 NativeInputEventReceiver 对象中,那么接下来就让我们来看到一下 该事件如何分发到具体的窗口处的。
2.0. runCommandsLockedInterruptible()

android/frameworks/…/InputDispatcher.cpp

bool InputDispatcher::runCommandsLockedInterruptible() {
    //保证mCommandQueue队列不为空
    if (mCommandQueue.empty()) {
        return false;
    }

    do {
        //取出 mCommandQueue 队列中的每一个 CommandEntry 对象
        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
        //取出该 CommandEntry 之后,会将其出队,即从 mCommandQueue 中删除
        mCommandQueue.pop_front();
        //再取出 commandEntry 中的 command 函数指针,使用该函数去处理当前的 commandEntry 事件
        Command command = commandEntry->command;
        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
        //处理完成后清除该 commandEntry
        commandEntry->connection.clear();
    } while (!mCommandQueue.empty());
    return true;
}
  • 可以看到,在该函数中,就是在遍历 mCommandQueue 队列中的元素,然后调用每一个 CommandEntry 元素的 command 函数,在完成调用之后,清空对应元素
  • 根据上面流程的分析,我们可以知道,mCommandQueue 队列元素中的 command 函数主要可能有两个:doPokeUserActivityLockedInterruptible()doInterceptKeyBeforeDispatchingLockedInterruptible(),那么接下来让我们分别来看一下这两个函数主要都做了些什么?
2.0.0. doPokeUserActivityLockedInterruptible()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
    mLock.unlock();
    //其中 mPolicy 就是 NativeInputManager 对象,这里就调用到了 NativeInputManager 中去
    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
    mLock.lock();
}
  • 从之前的 InputDispatcher 创建流程分析中,我们知道 InputDispatcher 对象中的 mPolicy 就是 NativeInputManager 对象,所以这里就是调用到了 NativeInputManager::pokeUserActivity() 函数
pokeUserActivity()

android/frameworks/…/com_android_service_input_InputManagerService.cpp

void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
    ATRACE_CALL();
    //这里就调用到了PowerManagerService 的JNI函数中
    android_server_PowerManagerService_userActivity(eventTime, eventType);
}
  • 这里就直接调用到 PowerMangerService 的JNI对象中
android_server_PowerManagerService_userActivity()

android/frameworks/…/com_android_server_power_PowerManagerService.cpp

void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
    //首先要判断JAVA层的 PMS 对象是否已经存在并完成注册
    if (gPowerManagerServiceObj) {
        //判断类型事件类型是否合法
        if (eventType >= 0 && eventType <= USER_ACTIVITY_EVENT_LAST) {
            ...
        }

        JNIEnv* env = AndroidRuntime::getJNIEnv();
        //调用 JAVA层的函数,即 PMS 中的 userActivityFromNative() 函数
        env->CallVoidMethod(gPowerManagerServiceObj,
                gPowerManagerServiceClassInfo.userActivityFromNative,
                nanoseconds_to_milliseconds(eventTime), eventType, 0);
        checkAndClearExceptionFromCallback(env, "userActivityFromNative");
    }
}
  • 其实这里就是通过 PMSJNI函数调用到 JAVA 层 PMS 对象中的 userActivityFromNative() 函数
userActivityFromNative()

android/frameworks/…/PowerManagerService.java

private void userActivityFromNative(long eventTime, int event, int flags) {
    userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
}
  • 至此就调用到了 JAVA 层的 PMS 当中来了,具体的处理逻辑不是本篇重点,就不展开叙述了
2.0.1. doInterceptKeyBeforeDispatchingLockedInterruptible()

android/frameworks/…/InputDispatcher.cpp

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;
    KeyEvent event = createKeyEvent(*entry);

    mLock.unlock();

    android::base::Timer t;
    //拿到对应窗口的 connection 连接
    sp<IBinder> token = commandEntry->inputChannel != nullptr
            ? commandEntry->inputChannel->getConnectionToken()
            : nullptr;
    //调用 NativeInputManager 中的函数
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    mLock.lock();
    //判断执行结果
    if (delay < 0) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry->interceptKeyWakeupTime = now() + delay;
    }
    entry->release();
}
  • 可以看到,最终又是调用到了 NativeInputManager 中的函数,还记得之前在 分析 notifyKey() 函数中详细解析的 interceptKeyBeforeQueueing() 函数,当前的 interceptKeyBeforeDispatching() 函数的调用逻辑链完全一致,最终就调用到了JAVA层的 PhoneWindowManager 对象当中,这里我们就不展开重复解析了

总结:

  • InputDispatcher 线程进行输入事件的分发的过程中,主要做了这几件事:
    1. 首先在 InputDispatcher 对象的 notifyKey() 接收到 InputReader 发送过来的输入事件,这样InputDispatcher线程就获取到了输入事件
    2. 接下来会先将 输入事件借由 interceptKeyBeforeQueueing() 函数进行入队列前处理,这个函数最终会调用到 PhoneWindowManager 中的 interceptKeyBeforeQueueing() 函数
    3. 在完成入队列的前期处理之后,就会调用 enqueueInboundEventLocked()函数将当前这个传输过来的输入事件,放入 mInboundQueue 待分发事件队列中,并且唤醒 InputDispatcher 线程的 Looper 循环(需要注意的是并不是放入事件一定会唤醒 looper 的,具体条件可见上面的详细分析))
    4. InputDispatcher 线程的 looper 循环被唤醒后,会调用 dispatchOnce() 函数从 mInboundQueue 待分发队列中取出输入事件,然后首先会将输入事件借由 pokeUserActivityLocked() ,构建出一个 CommandEntry 对象,并存放入 mCommandQueue 队列中,等待后面遍历队列进行处理(该消息最后会传给 PMS
    5. 在完成首个 CommandEntry 对象入队列 mCommandQueue 队列后,还会构建另一个 CommandEntry 对象,在构建完成后,先通过通过 findFocusedWindowTargetsLocked() 函数获取输入事件的目标分发窗口,找到后将和该窗口传输数据的 InputChannel 对象存放起来,而刚刚构建出来的 CommandEntry 对象也会存有该目标窗口的 InputChannel 对象,然后将该 CommandEntry 对象存放到 mCommandQueue 队列中,同样等待后面遍历队列进行处理(该消息最后会传给 PhoneWindowManager
    6. 在正确获取到目标窗口后,还会将当前的输入事件存放到对应窗口的分发队列中,也就是 InputChannel->Connection 对象中的 outboundQueue 队列中
    7. 之后就是遍历 outboundQueue 队列进行事件的分发,通过 publishKeyEvent() 函数将该输入事件写入 Server 端的 InputChannelSocket
    8. 在写入 Socket 成功后,接下来就会将该输入事件 从 outboundQueue 队列中删除,转而将该输入事件存放到 InputChannel->ConnectionwaitQueue 队列中,等待该事件处理完成
    9. 至此算是完成了输入事件向目标窗口分发的动作,接下来会遍历 mCommandQueue 队列,将之前存在其中的两个 CommandEntry 消息,按照要求分别发送到 PMSPhoneWindowManager 中,至此输入事件在 InputDispatcher 对象中的分发流程全部完成
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值