系列文章目录
本人系列文章-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的最大值:1844674407370955161
nextWakeupTime = 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派发策略时是false
break;
}
default:
break;
}
if (done) {//无论是成功派发还是事件被丢弃,都返回 true,当要询问key派发策略时是false
mLastDropReason = 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_POLICY
if (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的最大值:1844674407370955161
nextWakeupTime = LONG_LONG_MIN;//立刻下次唤醒。
}
} // release lock
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机制时讨论)。
}
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();//清除connection
delete 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,表示等会再询问派发策略,并设置interceptKeyWakeupTime
entry->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 lock
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机制时讨论)。
}
此时我们继续查看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派发策略或者此事件没有处理时是false
break;
}
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_IS
target.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_IS
inputTargetFlags = (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和flag
dispatchEntry->resolvedFlags = keyEntry->flags;
//此函数的作用主要是检查一个抬起的按键之前是否有按下过,和将按下的按键添加到一个容器中
//返回值为ture,代表是抬起的按键,之前有按下过。返回false,则代表up按键无按下过,不一致的事件,则删除
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
delete 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,然后赋值给seq
eventEntry(eventEntry),//保存的EventEntry对象
targetFlags(targetFlags),//发送的flag
xOffset(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);//如果从容器中找到此案件之前按下的事件,则移除按下的事件,并返回ture
if (index >= 0) {
mKeyMementos.removeAt(index);//mKeyMementos存储了所有按下还未抬起的key事件
return true;
}
//我们不能只放弃按键事件,因为这会阻止创建弹出窗口,当按键被按下时,弹出窗口会自动显示,然后在按键被释放时被关闭。
//问题是弹出窗口将不会收到原始的向下键,因此向上键将被视为与其观察到的状态不一致。
//我们也许可以通过合成一个向下键来处理这个问题,但这会导致其他问题。
//因此,现在,允许分发不一致的按键up事件。
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
"keyCode=%d, scanCode=%d",
entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
#endif
return 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) {//默认是null
injectionState->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;//设备id
msg.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 header
msg->header.type = header.type;
// Write the body
switch(header.type) {
case InputMessage::TYPE_KEY: {
// uint32_t seq
msg->body.key.seq = body.key.seq;
// nsecs_t eventTime
msg->body.key.eventTime = body.key.eventTime;
// int32_t deviceId
msg->body.key.deviceId = body.key.deviceId;
// int32_t source
msg->body.key.source = body.key.source;
// int32_t displayId
msg->body.key.displayId = body.key.displayId;
// int32_t action
msg->body.key.action = body.key.action;
// int32_t flags
msg->body.key.flags = body.key.flags;
// int32_t keyCode
msg->body.key.keyCode = body.key.keyCode;
// int32_t scanCode
msg->body.key.scanCode = body.key.scanCode;
// int32_t metaState
msg->body.key.metaState = body.key.metaState;
// int32_t repeatCount
msg->body.key.repeatCount = body.key.repeatCount;
// nsecs_t downTime
msg->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的处理流程】,在这一篇主要介绍一下应用层的处理流程。