Android输入子系统之InputDispatcher分发键盘消息过程分析
来源:互联网 发布:淘宝网买东西的步骤 编辑:程序博客网 时间:2024/05/30 07:12
InputDispatcher分发键盘消息过程分析
在Android输入子系统之启动过程分析中,InputManagerService启动之后,会调用start函数,之后调用native层的nativeStart函数,然后调用InputManager::start函数,该函数会调用InputDispatcherThread的threadLoop函数,该函数会调用InputDispatcher的dispatchOnce函数进行一次按键分发,如果没有按键事件发生,则睡眠。
Step 1. InputDispatcher.dispatchOnce
这个函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); mDispatcherIsAliveCondition.broadcast(); // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis);}
上述函数主要是调用dispatchOnceInnerLocked来进行一次按键分发,当没有按键消息时会走到mLooper->pollOnce(timeoutMillis);这个函数会进入睡眠状态,当有按键消息发生时该函数会返回,然后走到dispatchOnceInnerLocked函数。这里mLooper->pollOnce为何会睡眠涉及到Android的Handler机制,后续再研究
Step 2. InputDispatcher.dispatchOnceInnerLocked
函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); ... // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { //当InputReader往队列中插入了一个读取的键盘消息后,此处的mInboundQueue就不为空 if (mInboundQueue.isEmpty()) { ... } else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); ... } ... } ... switch (mPendingEvent->type) { ... case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); ... done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); 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 }}
从前文InputReader读取键盘消息过程分析 InputReader读取到一个消息后会调用KeyboardInputMapper的processKey,该函数会调用InputDispatcher的notifyKey函数,然后InputDispatcher会调用enqueueInboundEventLocked函数,将EventEntry加入到mInboundQueue中,然后调用mLooper->wake函数会唤醒InputDispatcherThread线程,InputDispatcher中把队列的第一个事件取出来,因为这里是键盘事件,所以mPendingEvent->type是EventEntry::TYPE_KEY,然后调用dispatchKeyLocked函数
Step 3. InputDispatcher.dispatchKeyLocked
该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ... // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); if (mFocusedWindowHandle != NULL) { commandEntry->inputWindowHandle = mFocusedWindowHandle; } commandEntry->keyEntry = entry; entry->refCount += 1; return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { if (*dropReason == DROP_REASON_NOT_DROPPED) { *dropReason = DROP_REASON_POLICY; } } ... // Identify targets. Vector<InputTarget> inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } setInjectionResultLocked(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } addMonitoringTargetsLocked(inputTargets); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true;}
这个函数主要做了下面三件事
A. 如果按键是第一次分发,则将命令封装为CommandEntry加入队列,后续执行doInterceptKeyBeforeDispatchingLockedInterruptible,以给java层拦截按键的机会
B. 找到当前激活的Window窗口,并将其加入到Vendor中
C. 找到需要主动监听按键的InputChannel,封装成InputTarget,加入到Vendor中
D. 将按键分发到上面的Vendor中的InputChannel中,这里存在多个
下面先分析如果将按键分发给InputChannel
Step 4. InputDispatcher.dispatchEventLocked
该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { ... for (size_t i = 0; i < inputTargets.size(); i++) { const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } } }
该函数主要是依次取出Vector中的InputTarget,根据InputTarget的InputChannel找到保存在mConnectionByFd中的Connection对象,并调用prepareDispatchCycleLocked函数进行分发
下面先分析如果将按键分发给InputChannel
Step 5. InputDispatcher.prepareDispatchCycleLocked
该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { ... // Not splitting. Enqueue dispatch entries for the event as is. enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);}
函数前面还有一些状态检查,这里默认都是通过的。最后enqueueDispatchEntriesLocked函数进行将connection分装成DispatchEntry,加入到connection->outboundQueue的队列中
Step 6. InputDispatcher::enqueueDispatchEntriesLocked
该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { bool wasEmpty = connection->outboundQueue.isEmpty(); // Enqueue dispatch entries for the requested modes. 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); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.isEmpty()) { startDispatchCycleLocked(currentTime, connection); }}
这个函数首先获取以前的connection的outboundQueue是否为空,然后将该事件调用enqueueDispatchEntryLocked将事件加入到outboundQueue中,如果以前为空,现在不为空,则调用startDispatchCycleLocked开始分发,如果以前的outboundQueue不为空,说明当前的Activity正在处理前面的按键,则不需要再调用startDispatchCycleLocked,因为只要开始处理,会等到队列为空才会停止。
Step 7. InputDispatcher.startDispatchCycleLocked
该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp 中
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.isEmpty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.head; dispatchEntry->deliveryTime = currentTime; // Publish the event. status_t status; EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); // Publish the key event. status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, keyEntry->eventTime); break; } ... } // Check the result. 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(), 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.#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());#endif connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " "status=%d", connection->getInputChannelName(), status); abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } return; } // Re-enqueue the event on the wait queue. connection->outboundQueue.dequeue(dispatchEntry); traceOutboundQueueLengthLocked(connection); connection->waitQueue.enqueueAtTail(dispatchEntry); traceWaitQueueLengthLocked(connection); }//end of while}
该函数从outboundQueue中取出需要处理的键盘事件,交给connection的inputPublisher去分发,之后将事件加入到connection的waitQueue中。分发事件是通过InputPublisher的publishKeyEvent来完成的。
Step 8. InputPublisher.publishKeyEvent
该函数定义在frameworks/native/libs/input/InputTransport.cpp
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) { InputMessage msg; msg.header.type = InputMessage::TYPE_KEY; msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; 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; return mChannel->sendMessage(&msg);}
该函数主要是将各个参数封装到InputMessage中,然后交给mChannel对象去分发
Step 9. InputChannel.sendMessage
该函数定义在frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; do { nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); return OK;}
该函数主要是通过send函数往socket的server端写入InputMessage对象,应用程序这一侧正睡眠在client端的fd上,此时client端就会收到该InputMessage,client会进行按键按键分发,应用程序这一侧的按键分发看下一篇。
至此InputDispatcher在server端的分发已经结束
- Android输入子系统之InputDispatcher分发键盘消息过程分析
- Android输入子系统之InputReader读取键盘消息过程分析
- Android输入子系统之应用程序注册消息监听过程分析
- Android输入子系统之启动过程分析
- Android 5.0输入系统分析之InputDispatcher线程分析
- Android6.0 按键流程(三)InputDispatcher分发输入消息
- Android6.0 按键流程 InputDispatcher分发输入消息(三)
- Android输入事件InputReader和InputDispatcher分析
- Android 输入系统之InputDispatcher篇
- Android JB 4.2 中InputManager 分发键盘消息给应用程序的过程分析--2 未完待续。。。。
- Android 5.0输入系统分析之InputReader和InputDispatcher线程启动过程
- 输入子系统匹配过程之list_for_each_entry()函数分析
- 输入子系统之触摸屏分析
- android输入子系统模型分析:
- android输入子系统模型分析:
- Android Input输入子系统分析
- Android之Input子系统事件分发流程
- Android之Input子系统事件分发流程
- 【R 数据科学】R语言进行数据科学整理最有用的包大全
- meta标签
- 《Core Java》读书笔记——第13章
- Web Storage 本地存储localStorage
- 关于gradle报错的问题
- Android输入子系统之InputDispatcher分发键盘消息过程分析
- Git简易指南
- Ubuntu16.04配置GPU caffe 加Qt下caffe和opencv环境配置
- 扫码登录
- sqlite 对表中的列进行增、删、查操作
- Linux下cronolog安装配置 分割catalina.out文件
- iOS 判断当前控制器是不是某个类(野路子)
- 基于BootstrapTable的简单应用
- Java开发之Servlet之间的跳转