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

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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值