系列文章目录
本人系列文章-CSDN博客
目录
系列文章目录
1.简介
1.1流程介绍
1.2 时序图
2.普通按键消息发送部分源码分析(按键按下事件)
2.1 开机后分发线程阻塞的地方
2.2 InputDispatcher::dispatchOnceInnerLocked
2.3 InputDispatcher::dispatchKeyLocked
2.4 InputDispatcher::dispatchOnce
2.5 runCommandsLockedInterruptible
2.6 InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible
2.7 InputDispatcher::dispatchOnce
2.8 InputDispatcher::dispatchKeyLocked
2.9 InputDispatcher::findFocusedWindowTargetsLocked
2.10 dispatchEventLocked
2.11 prepareDispatchCycleLocked
2.12 enqueueDispatchEntriesLocked
2.13 enqueueDispatchEntryLocked
2.14 startDispatchCycleLocked
2.15 InputPublisher::publishKeyEvent
2.16 InputChannel::sendMessage
1.简介
从上一篇幅我们知道了,普通按键事件在inputreader线程中会将按下事件和抬起事件放入mInboundQueue队列中,然后唤醒InputDispatcher线程去进行派发。本篇我们便来介绍一下InputDispatcher线程是如何派发的。
1.1流程介绍
1.当InputDispatcher线程被唤醒时,第一次执行dispatchOnce时,会生成一个去查询按键事件派发策略的命令并放入命令队列中,然后开始第二次循环。
2.在第二次循环的时候,会从命令队列中取出命令,然后通过jni走到IMS,再走到WMS,再走到phonewindowMnager中询问此按键的派发策略。然后开始第三次循环。
3.如果询问的按键派发策略是立即派发,则会寻找焦点窗口。
4.查找到合适的目标窗口后,会从保存窗口信息的容器中取出窗口信息,窗口信息保存着socket的然后通过sokcet对,将按键消息从IMS发送到应用程序端。
此sokcet对是应用在创建的时候,会调用WMS的addView函数,此函数会创建两个socket对,其中一个fd通过binder通信返回给应用端,应用端会将此fd监听查看是否有消息,另外一个会传递给IMS,IMS也会监听其fd是否有消息。
1.2 时序图
(图片可保存到本地放大观看)
2.普通按键消息发送部分源码分析(按键按下事件)
为了便于读者清晰的了解发送流程,此章节会删除不执行的代码,便于理解。
2.1 开机后分发线程阻塞的地方
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;//下一次唤醒该线程的时间,也可以说是下一次线程循环的执行时间点{ mDispatcherIsAliveCondition.broadcast();//分发线程处于活跃状态,进行广播if (!haveCommandsLocked()) {//此时无命令。作用:检查inputdispatcher的缓存队列中是否有还未处理的命令,//只有无命令时才会执行dispatchOnceInnerLocked方法,进行事件的分发。dispatchOnceInnerLocked(&nextWakeupTime);// 传入的nextWakeupTime决定了下次派发线程循环的执行时间点,}if (runCommandsLockedInterruptible()) {//如果此时缓存队列中有命令,则立即执行该命令,//并将下一次线程循环的执行时间点设置为LONG_LONG_MIN,将使派发线程立刻开始下次线程。//最小值定义为0,最大值是unsigned long long的最大值:1844674407370955161nextWakeupTime = LONG_LONG_MIN;}} nsecs_t currentTime = now();//获取当前时间点int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//下一次唤醒时间点-当前时间点=线程休眠时间mLooper->pollOnce(timeoutMillis);//调用pollOnce使线程休眠,等待回调,唤醒或超时。//Looper的pollOnce()的实质就是epoll_wait()。 因此派发线程的休眠在三种情况下可能被唤醒:1.调用Looper::wake()函数主动唤醒(有输入事件注入派发队列中时),2.到达nextWakeupTime的事件点时唤醒3.epoll_waitepoll_wait()监听的fd有epoll_event发生时唤醒(这种唤醒方式将在介绍ANR机制时讨论)。
}
从上一篇我们知道,当 inputreader线程向mInboundQueue中每次插入消息后,都会调用wake来唤醒分发线程。我们首先看一下按键按下的事件分发流程。
2.2 InputDispatcher::dispatchOnceInnerLocked
此函数的主要作用是:
1.取出消息,然后通过按键的派发策略,和时间等生成对应的分发策略,默认为不丢弃。
2.然后再调用dispatchKeyLocked进行事件的派发。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();//如果没有待发送的事件,则从mInboundQueue队列中取出一个事件,进行分发if (! mPendingEvent) {}else {//从派发队列中将位于队首的一条EventEntry取出并保存在mPendingEvent成员变量中。 // mPendingEvent表示处于派发过程中的一个输入事件mPendingEvent = mInboundQueue.dequeueAtHead();traceInboundQueueLengthLocked();//更新一下队列长度}if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) //设备消息类型为发送给应用程序{//判断此key是否需要发送到应用程序。pokeUserActivityLocked(mPendingEvent);//用来决定是否要唤醒设备或者点亮屏幕,最终调用的是PowerManagerService。}resetANRTimeoutsLocked();//重置ANR超时时间}bool done = false;DropReason dropReason = DROP_REASON_NOT_DROPPED;//默认丢弃原因为不丢弃switch (mPendingEvent->type) {case EventEntry::TYPE_KEY: {//按键类型KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//进一步分发按键事件,结果赋值给 done// 无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是falsebreak;}default:break;}if (done) {//无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是falsemLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately}
}
2.3 InputDispatcher::dispatchKeyLocked
此函数的作用是:
1.生成一个commond命令,并入队命令队列,询问此按键按下事件的分发策略。注意,一共是有两个策略,一个是派发策略,派发策略主要是询问是否是系统消费,是否要派发给应用程序等,而此时的分发策略是询问是否丢弃派发,是否等会派发等(如组合按键)
2.终止此时按键事件的派发,开启下一轮循环,先询问按键的分发策略,再派发此按键按下事件。
//此次派发会生成一个commond命令,并入队。去询问key的派发策略,因此此key事件咱未派发。commandEntry->keyEntry = entry;中保存了此次要派发的entry的信息。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {// Preprocessing.if (! entry->dispatchInProgress) {//dispatchInProgress默认为false,表示事件是否正在派发entry->dispatchInProgress = true;//设置事件为正在派发中}// 给policy一个机会,去截断key.//如果事件的interceptKeyResult是不知道,但是事件的policyFlags是要发送到应用程序的,则生成一个CommandEntry,然后返回false,等待命令的执行//如果事件的interceptKeyResult是不知道,事件不是发送给应用程序的,则设置falg为INTERCEPT_KEY_RESULT_CONTINUE。//如果事件的interceptKeyResult是INTERCEPT_KEY_RESULT_SKIP,表示跳过此事件,则设置其丢弃原因为DROP_REASON_POLICYif (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {//初始化的时候值是INTERCEPT_KEY_RESULT_UNKNOWN//如果此事件尚未进行过派发策略查询,则通过发送一个命令的方式查询派发策路 if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {CommandEntry* commandEntry = postCommandLocked(//生成了一个commandEntry& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);commandEntry->keyEntry = entry;entry->refCount += 1;return false; // 终止此次派发,开始下一轮的循环等待策略查询完成}}
}
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {CommandEntry* commandEntry = new CommandEntry(command);mCommandQueue.enqueueAtTail(commandEntry);//入队命令队列return commandEntry;
}
此时我们回到 2.2 InputDispatcher::dispatchOnceInnerLocked章节,所以我们可以看出来,第一次派发的按键时,要询问该按键的派发策略,所以done的值是flase
//此时回到上面928行的这个函数,注意此时命令队列中存在一条询问key事件派发策略的命令。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//进一步分发按键事件,结果赋值给 done// 无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是false/**//此时是false,if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately}*/
}
2.4 InputDispatcher::dispatchOnce
然后开启了下一次循环,此时命令队列中存在查询按键按下的分发策略的命令,所以执行runCommandsLockedInterruptible函数
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;//下一次唤醒该线程的时间,也可以说是下一次线程循环的执行时间点{ AutoMutex _l(mLock);mDispatcherIsAliveCondition.broadcast();//分发线程处于活跃状态,进行广播/*if (!haveCommandsLocked()) {//此时有命令。作用:检查inputdispatcher的缓存队列中是否有还未处理的命令,//只有无命令时才会执行dispatchOnceInnerLocked方法,进行事件的分发。dispatchOnceInnerLocked(&nextWakeupTime);// 传入的nextWakeupTime决定了下次派发线程循环的执行时间点,}*/if (runCommandsLockedInterruptible()) {//如果此时缓存队列中有一条询问key事件派发策略的命令,则立即执行该命令,//并将下一次线程循环的执行时间点设置为LONG_LONG_MIN,将使派发线程立刻开始下次线程。//最小值定义为0,最大值是unsigned long long的最大值:1844674407370955161nextWakeupTime = LONG_LONG_MIN;//立刻下次唤醒。}} // release locknsecs_t currentTime = now();//获取当前时间点int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//下一次唤醒时间点-当前时间点=线程休眠时间mLooper->pollOnce(timeoutMillis);//调用pollOnce使线程休眠,等待回调,唤醒或超时。//Looper的pollOnce()的实质就是epoll_wait()。 因此派发线程的休眠在三种情况下可能被唤醒:1.调用Looper::wake()函数主动唤醒(有输入事件注入派发队列中时),2.到达nextWakeupTime的事件点时唤醒3.epoll_waitepoll_wait()监听的fd有epoll_event发生时唤醒(这种唤醒方式将在介绍ANR机制时讨论)。
}
2.5 runCommandsLockedInterruptible
此函数的主要作用是:
1.取出命令,执行doInterceptKeyBeforeDispatchingLockedInterruptible函数,查询分发策略。
bool InputDispatcher::runCommandsLockedInterruptible() {do {CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();//取出命令Command command = commandEntry->command;(this->*command)(commandEntry); // 执行doInterceptKeyBeforeDispatchingLockedInterruptible函数,并传入commandEntry作为参数commandEntry->connection.clear();//清除connectiondelete commandEntry;} while (! mCommandQueue.isEmpty());return true;
}
2.6 InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible
此处简单介绍一下:
会先通过JNI走到IMS中,IMS会调用到WMS中,最终会调用到phoneWindowManager的interceptKeyBeforeDispatching函数,此函数内部会返回delay。
1.正常应用获取焦点时(即应用在前台交互),那么事件则可以继续派发。
2.比如我们现在按下了手机的音量按键,有可能存在接下来按键是电源按键,则存在是组合按键实现截屏的功能,因此现在系统也不知道此时是控制音量,还是截屏,所以需要等一会再询问此按键派发策略。
3.如果此时系统或者焦点(即应用程序没有焦点),则派发给应用是无效的,所以该按键丢弃。
//向mPolicy询问,
//如果delay小于0,则设置为INTERCEPT_KEY_RESULT_SKIP,代表丢弃该事件
//如果delay=0,则设置为INTERCEPT_KEY_RESULT_CONTINUE,代表事件可以继续派发
//如果delay>0,则设置为INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,并设置interceptKeyWakeupTime,表示等会再询问派发策略
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) {KeyEntry* entry = commandEntry->keyEntry;//从命令队列中取出key entry事件KeyEvent event;initializeKeyEvent(&event, entry);mLock.unlock();android::base::Timer t;nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,&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) {//如果delay小于0,则设置为INTERCEPT_KEY_RESULT_SKIP//代表丢弃该事件entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;} else if (!delay) {如果delay=0,则设置为INTERCEPT_KEY_RESULT_CONTINUE//代表事件可以继续派发entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;} else {//如果delay>0,则设置为INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,表示等会再询问派发策略,并设置interceptKeyWakeupTimeentry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;entry->interceptKeyWakeupTime = now() + delay;}entry->release();
}
2.7 InputDispatcher::dispatchOnce
然后会询问完按键按下事件的派发策略后,会立刻唤醒派发循环,开始派发此按键按下的事件。
//执行查询派发key事件策略的命令后,会立即执行下一次循环。
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;//下一次唤醒该线程的时间,也可以说是下一次线程循环的执行时间点{ AutoMutex _l(mLock);mDispatcherIsAliveCondition.broadcast();//分发线程处于活跃状态,进行广播if (!haveCommandsLocked()) {//作用:检查inputdispatcher的缓存队列中是否有还未处理的命令//只有无命令时才会执行dispatchOnceInnerLocked方法,进行事件的分发。dispatchOnceInnerLocked(&nextWakeupTime);// 传入的nextWakeupTime决定了下次派发线程循环的执行时间点}} // release locknsecs_t currentTime = now();//获取当前时间点int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//下一次唤醒时间点-当前时间点=线程休眠时间mLooper->pollOnce(timeoutMillis);//调用pollOnce使线程休眠,等待回调,唤醒或超时。//Looper的pollOnce()的实质就是epoll_wait()。 因此派发线程的休眠在三种情况下可能被唤醒://1.调用Looper::wake()函数主动唤醒(有输入事件注入派发队列中时),//2.到达nextWakeupTime的事件点时唤醒//3.epoll_waitepoll_wait()监听的fd有epoll_event发生时唤醒(这种唤醒方式将在介绍ANR机制时讨论)。
}
此时我们继续查看dispatchOnceInnerLocked函数
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();//此时不为空,此时mPendingEvent是之前key按下事件,在上一次返回flse时候,没有释放此事件if (! mPendingEvent) {//如果没有待发送的事件,则从mInboundQueue队列中取出一个事件,进行分发}bool done = false;DropReason dropReason = DROP_REASON_NOT_DROPPED;//默认丢弃原因为不丢弃switch (mPendingEvent->type) {case EventEntry::TYPE_KEY: {//按键类型KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//进一步分发按键事件,结果赋值给 done// 无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略或者此事件没有处理时是falsebreak;}if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately}
}
2.8 InputDispatcher::dispatchKeyLocked
此函数的主要作用是:
1.通过findFocusedWindowTargetsLocked从保存窗口信息的列表中,找到合适的派发窗口。
2.dispatchEventLocked向指定窗口派发按键按下的事件。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {// Preprocessing.if (! entry->dispatchInProgress) {//此时dispatchInProgress为true}Vector<InputTarget> inputTargets;//inputTargets是一个容器,会存储目标窗口的信息,根据key事件的类型,寻找合适的目标窗口。//其返回值injectionResult指明寻找结果,而找到的合适的目标窗口信息将被保存在inputTargets列表中int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime);setInjectionResultLocked(entry, injectionResult);//将injectionResult结果赋值到event内部的一个属性中///addMonitoringTargetsLocked(inputTargets);//默认mMonitoringChannels是false// Dispatch the key.dispatchEventLocked(currentTime, entry, inputTargets);return true;
}
2.9 InputDispatcher::findFocusedWindowTargetsLocked
按键的派发相较于motion事件的派发比较简单,按键事件的派发只需要派发给获取焦点的应用程序即可,获取焦点的应用程序即:用户正在交互使用的应用程序。
此函数的作用是:
1.检查目标窗口的权限和是否已经准备就绪。
2.当窗口准备好了以后,addWindowTargetLocked会将焦点窗口的相关信息保存到inputTargets中。
那么此时读者会疑问,焦点窗口是什么时候被设置到input中的,此篇仅简单介绍一下:
当窗口发生变化时,WMS会调用InputMonitor类的updateInputWindowsLw函数,而InputMonitor类是IMS的回调类,于是便通过jni更新窗口信息到InputDispatcher中。
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {int32_t injectionResult;std::string reason;// 如果有焦点窗口,则检查权限,权限检查失败,则if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;goto Failed;}// 检查窗口是否准备好进行更多输入事件reason = checkWindowReadyForMoreInputLocked(currentTime,mFocusedWindowHandle, entry, "focused");if (!reason.empty()) {//如果reason不为空,则可能发生了anr,用handleTargetsNotReadyLocked去判断injectionResult = handleTargetsNotReadyLocked(currentTime, entry,mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.c_str());goto Unresponsive;}//代表此时有焦点窗口,并且权限通过,并且准备好接受输入事件injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;//给flag赋值addWindowTargetLocked(mFocusedWindowHandle,InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),inputTargets);//addWindowTargetLocked 函数将接收按键事件的目标窗口的一些信息保存到 inputTargets容器中//参数分析://mFocusedWindowHandle是焦点窗口//InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS代表,是前台窗口和必须原样发送事件//0//inputTargets保存目标窗口的信息的容器,此时还是空// Done.
Failed:
Unresponsive:nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);//timeSpentWaitingForApplication是等待应用程序花费的时间updateDispatchStatisticsLocked(currentTime, entry,injectionResult, timeSpentWaitingForApplication);return injectionResult;
}
bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,const InjectionState* injectionState) {/* if (injectionState//injectionState代表是否是从IMS注入的事件,默认物理按键是null&& (windowHandle == NULL|| windowHandle->getInfo()->ownerUid != injectionState->injectorUid)&& !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {if (windowHandle != NULL) {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;
}
//从windowHandle的句柄中获取getInfo,然后将其inputchannel等信息生成InputTarget类对象,然后保存到inputTargets容器中
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {inputTargets.push();const InputWindowInfo* windowInfo = windowHandle->getInfo();InputTarget& target = inputTargets.editTop();target.inputChannel = windowInfo->inputChannel;//获取发送窗口的通道target.flags = targetFlags;//此时按键的flag是InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IStarget.xOffset = - windowInfo->frameLeft;//窗口坐标的左target.yOffset = - windowInfo->frameTop;窗口坐标的顶部target.scaleFactor = windowInfo->scaleFactor;//窗口的缩放系数target.pointerIds = pointerIds;//pointerIds是0
}
2.10 dispatchEventLocked
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {pokeUserActivityLocked(eventEntry);//保持屏幕唤醒for (size_t i = 0; i < inputTargets.size(); i++) {//此时inputTargets中只有一个焦点窗口const InputTarget& inputTarget = inputTargets.itemAt(i);ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);//此函数的作用是检查此窗口的inputchannel是否已注册,//如果mConnectionsByFd中存在对应的connection,则返回索引if (connectionIndex >= 0) {sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);//获取此窗口的connection类的对象prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);}}
}
2.11 prepareDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection> &connection, EventEntry *eventEntry, const InputTarget *inputTarget)
{//不分割,按照原样事件进行入队分发enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
2.12 enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection> &connection, EventEntry *eventEntry, const InputTarget *inputTarget)
{bool wasEmpty = connection->outboundQueue.isEmpty();//此时是空// 调用了六次 enqueueDispatchEntryLocked 函数,仅仅最后一个参数不同,但不是执行六次,//而是根据inputTarget的flags和最后一个参数是否相等,如果相等则处理,不相等会返回。/**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);//key事件的flags是InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,//如果是key事件,则只有这个函数执行了,此函数看名字感觉是将entry入队/**enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);*/// 当原先的 outbound 队列为空, 且当前 outbound 不为空的情况执行,开始循环分发if (wasEmpty && !connection->outboundQueue.isEmpty()){startDispatchCycleLocked(currentTime, connection);}
}
2.13 enqueueDispatchEntryLocked
此函数的作用是:
1.将EventEntry 保存到 DispatchEntry中。
2.判断按键是否是抬起按键,如果按键是抬起按键,则判断之前此按键是否按下过,如果没有,则删除此抬起按键。
3.将DispatchEntry按键事件放入outboundQueue的队列中。
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection> &connection, EventEntry *eventEntry, const InputTarget *inputTarget,int32_t dispatchMode)
{int32_t inputTargetFlags = inputTarget->flags;//此时的获取的flag是InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_ISinputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;//FLAG_DISPATCH_MASK是所有的flag集合,dispatchMode此时传入的是原样发送//这是一个新的事件。//将 EventEntry 转换为 DispatchEntry, 在后面会加入 connection 的 outbound 队列DispatchEntry *dispatchEntry = new DispatchEntry(eventEntry, inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,inputTarget->scaleFactor);//应用target标志并更新连接的输入状态。switch (eventEntry->type){case EventEntry::TYPE_KEY:{KeyEntry *keyEntry = static_cast<KeyEntry *>(eventEntry);dispatchEntry->resolvedAction = keyEntry->action;//从keyEntry中设置其action和flagdispatchEntry->resolvedFlags = keyEntry->flags;//此函数的作用主要是检查一个抬起的按键之前是否有按下过,和将按下的按键添加到一个容器中//返回值为ture,代表是抬起的按键,之前有按下过。返回false,则代表up按键无按下过,不一致的事件,则删除if (!connection->inputState.trackKey(keyEntry,dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)){
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",connection->getInputChannelName().c_str());
#endifdelete dispatchEntry;return; // skip the inconsistent event}break;}}// 我们正在等待此dispatchEntry事件的分发完成if (dispatchEntry->hasForegroundTarget())//是要发送到前台,所以是执行的如果此事件是要发送到前台窗口{incrementPendingForegroundDispatchesLocked(eventEntry);//增加未处理的前台分发数量}// 将dispatchEntry事件放入connection对象的outboundQueue序列的队尾connection->outboundQueue.enqueueAtTail(dispatchEntry);traceOutboundQueueLengthLocked(connection);//追踪队列的长度
}
InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :seq(nextSeq()),//nextSeq是一个静态函数,里面有一个int值的静态变量,每构造一个DispatchEntry对象,此值就加1,然后赋值给seqeventEntry(eventEntry),//保存的EventEntry对象targetFlags(targetFlags),//发送的flagxOffset(xOffset), //目标窗口的x偏移量yOffset(yOffset), //目标窗口的y偏移量scaleFactor(scaleFactor),//缩放比例deliveryTime(0), resolvedAction(0), resolvedFlags(0) {eventEntry->refCount += 1;}
bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,int32_t action, int32_t flags) {switch (action) {/**case AKEY_EVENT_ACTION_UP: {if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {for (size_t i = 0; i < mFallbackKeys.size(); ) {if (mFallbackKeys.valueAt(i) == entry->keyCode) {mFallbackKeys.removeItemsAt(i);} else {i += 1;}}}ssize_t index = findKeyMemento(entry);//如果从容器中找到此案件之前按下的事件,则移除按下的事件,并返回tureif (index >= 0) {mKeyMementos.removeAt(index);//mKeyMementos存储了所有按下还未抬起的key事件return true;}//我们不能只放弃按键事件,因为这会阻止创建弹出窗口,当按键被按下时,弹出窗口会自动显示,然后在按键被释放时被关闭。//问题是弹出窗口将不会收到原始的向下键,因此向上键将被视为与其观察到的状态不一致。//我们也许可以通过合成一个向下键来处理这个问题,但这会导致其他问题。//因此,现在,允许分发不一致的按键up事件。#if DEBUG_OUTBOUND_EVENT_DETAILSALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, ""keyCode=%d, scanCode=%d",entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
#endifreturn false;return true;}*/case AKEY_EVENT_ACTION_DOWN: {//如果按键是按下,则用findKeyMemento查找是否已经有此按键按下的事件,如果有,//则表示产生了连续两个按下的事件,故移除上一个按下的按键,并通过addKeyMemento函数将这次按下的按键保存到容器中ssize_t index = findKeyMemento(entry);if (index >= 0) {mKeyMementos.removeAt(index);}addKeyMemento(entry, flags);return true;}default:return true;}
}
void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {mKeyMementos.push();KeyMemento& memento = mKeyMementos.editTop();memento.deviceId = entry->deviceId;memento.source = entry->source;memento.keyCode = entry->keyCode;memento.scanCode = entry->scanCode;memento.metaState = entry->metaState;memento.flags = flags;memento.downTime = entry->downTime;memento.policyFlags = entry->policyFlags;
}
inline bool hasForegroundTarget() const {return targetFlags & InputTarget::FLAG_FOREGROUND;
}
void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {InjectionState* injectionState = entry->injectionState;/*if (injectionState) {//默认是nullinjectionState->pendingForegroundDispatches += 1;//pendingForegroundDispatches默认值是0,表示正在进行的前台分发的数量}*/
}
2.14 startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {//当connection的状态正常且outboundQueue不为空时while (connection->status == Connection::STATUS_NORMAL&& !connection->outboundQueue.isEmpty()) {DispatchEntry* dispatchEntry = connection->outboundQueue.head;//取出队列头部的dispatchEntry事件dispatchEntry->deliveryTime = currentTime;//完成发送的时间// Publish the event.status_t status;EventEntry* eventEntry = dispatchEntry->eventEntry;//取出EventEntry类型的事件switch (eventEntry->type) {case EventEntry::TYPE_KEY: {KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);// Publish the key event.status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,//DispatchEntry是一个链表,seq表示发送事件的序列号。keyEntry->deviceId, keyEntry->source,dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,keyEntry->keyCode, keyEntry->scanCode,keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,keyEntry->eventTime);//调用了Connection类中的InputPublisher类对象的publishKeyEvent方法。//参数分析://dispatchEntry->seq,请求序列,seq表示发送事件的序列号。//keyEntry->deviceId,设备id//keyEntry->source,是KeyboardInputMapper初始化时赋值的,//其值是INPUT_DEVICE_CLASS_KEYBOARD,INPUT_DEVICE_CLASS_DPAD从,INPUT_DEVICE_CLASS_GAMEPAD的某一个//dispatchEntry->resolvedAction=keyEntry->action,表示按下或者抬起//dispatchEntry->resolvedFlags,表示flag策略,此时应该是POLICY_FLAG_PASS_TO_USER//keyEntry->keyCode,对应的按键码//keyEntry->scanCode,按键码对应的扫描码break;}}// 检查发送的结果if (status) {if (status == WOULD_BLOCK) {if (connection->waitQueue.isEmpty()) {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 {// Pipe is full and we are waiting for the app to finish process some events// before sending more events to it.connection->inputPublisherBlocked = true;}} else {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;}// Re-enqueue the event on the wait queue.connection->outboundQueue.dequeue(dispatchEntry);//发送完成后,将此dispatchEntry退出outboundQueue队列traceOutboundQueueLengthLocked(connection);//追踪outboundQueue队列长度connection->waitQueue.enqueueAtTail(dispatchEntry);//将此dispatchEntry入队到waitQueue队列中traceWaitQueueLengthLocked(connection);//追踪waitQueue队列的长度}
}
2.15 InputPublisher::publishKeyEvent
主要作用是:
1.将消息封装成InputMessage,然后通过InputChannel发送消息。
status_t InputPublisher::publishKeyEvent(uint32_t seq,int32_t deviceId,int32_t source,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 msg;msg.header.type = InputMessage::TYPE_KEY;//表示按键事件msg.body.key.seq = seq;//消息的序列号msg.body.key.deviceId = deviceId;//设备idmsg.body.key.source = source;//表示键盘等msg.body.key.action = action;//按下或者抬起msg.body.key.flags = flags;//表示policy,发送给应用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;return mChannel->sendMessage(&msg);
}
2.16 InputChannel::sendMessage
此处便是真正调用socket send接口发送消息的地方。
status_t InputChannel::sendMessage(const InputMessage* msg) {const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);//InputMessage字段之间可能有非零字节。//强制将整个内存初始化为零,然后仅在每个字段的基础上复制有效字节。ssize_t nWrite;do {nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR);//如果出错,则一直循环发送等到其成功为止//函数说明:send()用来将数据由指定的socket 传给对方主机. //参数s 为已建立好连接的socket. //参数msg 指向发送的数据内容. //参数len 则为数据长度. //参数flags 一般设0, 其他数值定义如下: //MSG_OOB 传送的数据以out-of-band 送出. //MSG_DONTROUTE 取消路由表查询 //MSG_DONTWAIT 设置为不可阻断运作 //MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断. //返回值:成功则返回实际传送出去的字符数, 失败返回-1. 错误原因存于errno.//当 mFd 写入消息后,此时会唤醒处于 epoll_wait 状态的应用进程的 UI 线程if (nWrite < 0) {//如果发送失败int error = errno;if (error == EAGAIN || error == EWOULDBLOCK) {return WOULD_BLOCK;}if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {return DEAD_OBJECT;}return -error;}if (size_t(nWrite) != msgLength) {return DEAD_OBJECT;}return OK;
}
size_t InputMessage::size() const {switch (header.type) {case TYPE_KEY:return sizeof(Header) + body.key.size();
}
}
//InputMessage字段之间可能有非零字节。强制将整个内存初始化为零,然后仅在每个字段的基础上复制有效字节。
void InputMessage::getSanitizedCopy(InputMessage* msg) const {memset(msg, 0, sizeof(*msg));//初始化所有字节为0// Write the headermsg->header.type = header.type;// Write the bodyswitch(header.type) {case InputMessage::TYPE_KEY: {// uint32_t seqmsg->body.key.seq = body.key.seq;// nsecs_t eventTimemsg->body.key.eventTime = body.key.eventTime;// int32_t deviceIdmsg->body.key.deviceId = body.key.deviceId;// int32_t sourcemsg->body.key.source = body.key.source;// int32_t displayIdmsg->body.key.displayId = body.key.displayId;// int32_t actionmsg->body.key.action = body.key.action;// int32_t flagsmsg->body.key.flags = body.key.flags;// int32_t keyCodemsg->body.key.keyCode = body.key.keyCode;// int32_t scanCodemsg->body.key.scanCode = body.key.scanCode;// int32_t metaStatemsg->body.key.metaState = body.key.metaState;// int32_t repeatCountmsg->body.key.repeatCount = body.key.repeatCount;// nsecs_t downTimemsg->body.key.downTime = body.key.downTime;break;}default: {LOG_FATAL("Unexpected message type %i", header.type);break;}}
}
此处涉及inputchannel的创建过程。
大致讲解一下:
1.应用程序app一定会有activity,然后在打开app时会调用Activity.onCreate,然后经过AMS,再经过WMS,会调用WindowManagerGlobal.addView(),这个函数里面会创建socket对,一个被注册到注册到 InputDispatcher 中,一个返回给app进程的客户端,app进程会监听此socket,当有事件发送到应用程序时,会回调 InputEventReceiver 对应的 native 层对象 NativeInputEventReceiver 的 handleEvent 函数。
此时,我们就通过socket发送按键消息到达了应用端,下一篇【android 9】【input】【9.发送按键事件3——Inputchannel的创建过程】,我们主要介绍一下应用端是在什么时候创建的sokcet进行的通信。然后在【android 9】【input】【10.发送按键事件4——view的处理流程】,在这一篇主要介绍一下应用层的处理流程。