touch 事件传递过程

620 阅读6分钟

入口 dispatchOnce

整体 touch 事件的分发都由 InputDispatcherdispatchOnce() 开始(InputReader 负责读事件,不负责分发)

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        
        // 判断 mCommandQueue 中是否有值,如果有就先执行 command
        if (!haveCommandsLocked()) {
            // 真正分发
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // If we are still waiting for ack on some events,
        // we might have to wake up earlier to check if an app is anr'ing.
        
        // 计算应用被阻塞多久。这与 java 层 Handler 原理一样
        // 没有当前要处理的消息时,会被阻塞。阻塞的时长由最近一个要处理的消息时间决定
        const nsecs_t nextAnrCheck = processAnrsLocked();
        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);

        // We are about to enter an infinitely long sleep, because we have no commands or
        // pending or queued events
        if (nextWakeupTime == LONG_LONG_MAX) {
            mDispatcherEnteredIdle.notify_all();
        }
    } // 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);
    
    // 通过 epoll 机制阻塞当前线程若干时间。如果中途有别的事件,会主动唤醒 
    mLooper->pollOnce(timeoutMillis);
}

涉及到两个很重要的方法 dispatchOnceInnerLockedprocessAnrsLocked,前者负责分发,后者会在超时时处理 anr。

分发

dispatchOnceInnerLocked

会首先从 mInboundQueue 取出一个事件(该 queue 中的事件由 InputReader 放入),同时使用 mPendingEvent 记录下该事件。随后根据事件类型调用不同的方法进行分发。此处以 key 事件为例,调用 dispatchKeyLocked

dispatchKeyLocked

首先调用 findFocusedWindowTargetsLocked 找到可以处理的窗口,然后调用 dispatchEventLocked 再进行分发,后者最主要是调用 prepareDispatchCycleLocked,然后到 enqueueDispatchEntriesLocked

enqueueDispatchEntriesLocked

主要是将事件添加至 connectionoutboundQueue 中,然后调用 startDispatchCycleLocked

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   std::shared_ptr<EventEntry> eventEntry,
                                                   const InputTarget& inputTarget) {
    // 通过 connection,Ims 可以将事件发送给应用进程
    bool wasEmpty = connection->outboundQueue.empty();

    // Enqueue dispatch entries for the requested modes.
    // 将 eventEntry 添加到 connection->outboundQueue 中
    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.empty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

startDispatchCycleLocked

该方法会真正通过 connection 将事件发给应用端,同时会将事件记录到 waitQueue 中。

// Re-enqueue the event on the wait queue.
// 从 outboundQueue 中删除
connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                            connection->outboundQueue.end(),
                                            dispatchEntry));
traceOutboundQueueLength(*connection);
// 加入到 waitQueue 中
connection->waitQueue.push_back(dispatchEntry);
if (connection->responsive) {
    // 将事件添加至 mAnrTracker 中
    mAnrTracker.insert(dispatchEntry->timeoutTime,
                       connection->inputChannel->getConnectionToken());
}

三个队列

touch 事件的整个流程中涉及到三个 queue:

  1. mInboundQueue:由 InputReader 添加,由 InputDispatcher 消费。存储的是 InputDispatcher 需要分发出去的事件
  2. outboundQueue:由 InputDispatcher 添加,由 connection 消费,它会将这里面的事件依次发给应用进程
  3. waitQueueconnection 将事件分发给应用进程后,会将事件暂存在该队列中,只有当应用端处理完成后才会从该队列中移除。

anr

关于这部分源码分析可看 gityuan 的文章

它的触发逻辑也在 dispatchOnce 中,它会调用 processAnrsLocked

nsecs_t InputDispatcher::processAnrsLocked() {
    const nsecs_t currentTime = now();
    nsecs_t nextAnrCheck = LONG_LONG_MAX;
    // Check if we are waiting for a focused window to appear. Raise ANR if waited too long

    // Check if any connection ANRs are due
    // 获取 mAnrTracker 记录的第一个事件的过时时间
    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
    // 如果没有过时,就意味着并没有 anr 发生
    // 所以 InputDispatcher 就可以休息一段时间
    if (currentTime < nextAnrCheck) { // most likely scenario
        return nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck
    }

    // If we reached here, we have an unresponsive connection.
    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
    connection->responsive = false;
    // Stop waking up for this unresponsive connection
    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
    // 触发 anr 逻辑
    onAnrLocked(connection);
    return LONG_LONG_MIN;
}

onAnrLocked

最核心的逻辑

// dump anr 相关的日志
updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
// 最关键的是调用 sendWindowUnresponsiveCommandLocked
processConnectionUnresponsiveLocked(*connection, std::move(reason));

processConnectionUnresponsiveLocked

构建一个 command,然后调用 postCommandLocked

void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
                                                          std::optional<int32_t> pid,
                                                          std::string reason) {
    auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
        scoped_unlock unlock(mLock);
        // command 的主要执行逻辑。
        // 它是调用 NativeInputManager::notifyWindowUnresponsive
        mPolicy->notifyWindowUnresponsive(token, pid, reason);
    };
    // 它会将 command 放到 mCommandQueue 中
    postCommandLocked(std::move(command));
}

回到最上面的 dispatchOnce 中,可以发现该 command 会先于事件执行。

现在流程到了 com_android_server_input_InputManagerService#notifyWindowUnresponsive,它里面会通过 Jni 调用到 java 层的 InputManagerServicenotifyWindowUnresponsive


private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,
        String reason) {
    // mWindowManagerCallbacks 只有 IMS#setWindowManagerCallbacks() 中被赋值
    mWindowManagerCallbacks.notifyWindowUnresponsive(token,
            isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
}
// SystemServer 中
// IMS 的初始化在 SystemServer 中,setWindowManagerCallbacks 也在 SystemServer 中调用
// wm 是 WindowManagerService 对象,返回的就是 InputManagerCallback 对象
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());

所以 anr 的处理到 InputManagerCallbacknotifyWindowUnresponsive 中,最终到 AMS 的 inputDispatchingTimedOut 中。新版本中再经过各种辗转到 ProcessErrorStateRecordappNotResponding,这里面的主要逻辑与 gityuan 类似。

应用端的回传

应用端通过 NativeInputEventReceiver::finishInputEvent 告知 ims 事件已消费

应用端在处理完 touch 事件后,需要通知到 ims,把事件从 waitQueue 中删除。现在就看一下应用端是如何处理的。

我们知道 ViewRootImplsetView 会调用到 WMS,由 WMS 构建一个 socketpair,其中一个 fd 传给应用端,另一个传给 ims。当 ims 中有消息需要处理时,就是通过 socketpair 传递给应用端的,也就是第一部分中 connection 内部的实现方式。

// ViewRootImpl#setView
// 此时 inputChannel 已经被填充了 socketpair 中的一个 fd
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
        Looper.myLooper());

构建一个 WindowInputEventReceiver 对象,它继承于 InputEventReceiver。后者在构造函数中会调用 nativeInit,然后在 native 层构造一个 NativeInputEventReceiver 对象,接着调用它的 initialize 方法。

status_t NativeInputEventReceiver::initialize() {
    // 向 native 层的 looper 添加一个 fd
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        // mInputConsumer 就是 InputChannel,所以这里是将 wms 生成的 socketpair 中的 fd 注册到本进程的 looper 中
        // 当 ims 有消息传来时,就会执行到自己的 handleEvent() 方法
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            // 将 callback 注册成自己,所以事件到来时会调用自己的 handleEvent() 
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

NativeInputEventReceiverhandleEvent 会调用 consumeEvents,该方法的具体分析见链接,最终到 java 层 InputEventReceiverdispatchInputEvent,后面经过各种 stage 到具体的 view 处理。

touch 事件经过中间的各种处理后,最终会到 InputEventReceiverfinishInputEvent,然后到 NativeInputEventReceiver::finishInputEvent

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    Finish finish{
            .seq = seq,
            .handled = handled,
    };
    mOutboundQueue.push_back(finish);
    // 会遍历 mOutboundQueue,如果是 Finish,就会调用 
    // mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
    return processOutboundEvents();
}

status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {

    // Send finished signals for the batch sequence chain first.
    size_t seqChainCount = mSeqChains.size();
    if (seqChainCount) {
        uint32_t currentSeq = seq;
        uint32_t chainSeqs[seqChainCount];
        size_t chainIndex = 0;
        for (size_t i = seqChainCount; i > 0; ) {
             i--;
             const SeqChain& seqChain = mSeqChains[i];
             if (seqChain.seq == currentSeq) {
                 currentSeq = seqChain.chain;
                 chainSeqs[chainIndex++] = currentSeq;
                 mSeqChains.erase(mSeqChains.begin() + i);
             }
        }
        status_t status = OK;
        while (!status && chainIndex > 0) {
            chainIndex--;
            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
        }
        if (status) {
            // An error occurred so at least one signal was not sent, reconstruct the chain.
            for (;;) {
                SeqChain seqChain;
                seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
                seqChain.chain = chainSeqs[chainIndex];
                mSeqChains.push_back(seqChain);
                if (!chainIndex) break;
                chainIndex--;
            }
            return status;
        }
    }

    // Send finished signal for the last message in the batch.
    return sendUnchainedFinishedSignal(seq, handled);
}

InputConsumer::sendFinishedSignal 最核心的一点就是调用 sendUnchainedFinishedSignal

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::Type::FINISHED;
    msg.header.seq = seq;
    msg.body.finished.handled = handled;
    msg.body.finished.consumeTime = getConsumeTime(seq);
    // 通过 channel 发送,最终到 InputDispatcher 中
    status_t result = mChannel->sendMessage(&msg);
    return result;
}

InputDispatcher 中经历最终到 InputDispatcher::handleReceiveCallback,后者调用 finishDispatchCycleLocked,它又会构建一个 command。在 handleReceiveCallback 中还会调用 runCommandsLockedInterruptable() 这个方法就会触发上面构建的 command 的执行

 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                const sp<Connection>& connection, uint32_t seq,
                                                bool handled, nsecs_t consumeTime) {
    // Notify other system components and prepare to start the next dispatch cycle.
    // 构建 command
    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
    };
    postCommandLocked(std::move(command));
}

所以最终事件从 waitQueue 中移除是调用 doDispatchCycleFinishedCommand


void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
                                                     const sp<Connection>& connection, uint32_t seq,
                                                     bool handled, nsecs_t consumeTime) {
    DispatchEntry* dispatchEntry = *dispatchEntryIt;
    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
    // 打印出处理时间超过 2s 的事件
    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
    }

    // Dequeue the event and start the next cycle.
    // Because the lock might have been released, it is possible that the
    // contents of the wait queue to have been drained, so we need to double-check
    // a few things.
    dispatchEntryIt = connection->findWaitQueueEntry(seq);
    if (dispatchEntryIt != connection->waitQueue.end()) {
        dispatchEntry = *dispatchEntryIt;
        // 将事件从 waitQueue 中移除
        connection->waitQueue.erase(dispatchEntryIt);
        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
    }

    // Start the next dispatch cycle for this connection.
    // 开始下一次处理
    startDispatchCycleLocked(now(), connection);
}