Android输入子系统之InputDispatcher分发键盘消息过程分析

来源:互联网 发布:淘宝网买东西的步骤 编辑:程序博客网 时间:2024/05/30 07:12

InputDispatcher分发键盘消息过程分析

在Android输入子系统之启动过程分析中,InputManagerService启动之后,会调用start函数,之后调用native层的nativeStart函数,然后调用InputManager::start函数,该函数会调用InputDispatcherThread的threadLoop函数,该函数会调用InputDispatcher的dispatchOnce函数进行一次按键分发,如果没有按键事件发生,则睡眠。
InputDispatcher分发键盘消息过程分析

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端的分发已经结束

阅读全文
0 0
原创粉丝点击