fwrite

fwrite

好好生活
twitter
github
email

Input輸入事件處理流程

安卓中輸入事件主要分為 KeyEvent 和 MotionEvent 兩種

傳遞流程#

webp

這裡首先 eventhub 構建的時候會遍歷整個 /dev/input 路徑下的 fd,並將其添加到 epoll 中,同時還會監聽此路徑下新的設備的創建和卸載。當 driver 向特定描述符寫入事件後,會觸發喚醒 epoll 起來工作,這時候 eventHub 通過 read 方法從描述符中讀取原始事件,然後通過簡單封裝成 rawEvent 並傳遞給 InputReader。
InputReader 中的 threadLoop 中會調用 eventHub 的 getEvents 來獲取輸入事件,然後通過調用 notifyxxx 方法將事件傳遞到 InputDispater 並最終通過 notifyxxx 方法傳遞到上層。

Java 層事件傳遞流程#

Input 輸入事件處理流程

Native 傳遞事件到 Java#

在 android_view_InputEventReceiver 中的 consumeEvents 方法中會從 InputConsumer 中獲取 input 事件,然後通過 jni 將事件向上層傳遞。

webp-1720679461808-171

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
              getInputChannelName().c_str(), toString(consumeBatches), frameTime);
    }
    // 省略若干行
    // 這裡通過InputConsumer的consume方法獲取到inputevent
    status_t status = mInputConsumer.consume(&mInputEventFactory,
            consumeBatches, frameTime, &seq, &inputEvent,
            &motionEventType, &touchMoveNum, &flag);
    // 省略若干行
    if (inputEventObj) {
        if (kDebugDispatchCycle) {
            ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
        }
        // 此處通過jni調用InputEventReceiver的dispatchInputEvent方法進行事件的分發,
        // 從而將input事件傳遞到java層
        env->CallVoidMethod(receiverObj.get(),
                gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
        if (env->ExceptionCheck()) {
            ALOGE("Exception dispatching input event.");
            skipCallbacks = true;
        }
        env->DeleteLocalRef(inputEventObj);
    } else {
        ALOGW("channel '%s' ~ Failed to obtain event object.",
                getInputChannelName().c_str());
        skipCallbacks = true;
    }
  
    if (skipCallbacks) {
        mInputConsumer.sendFinishedSignal(seq, false);
    }
}
 

InputEventReceiver 分發事件#

在 InputEventReceiver 中的 dispatchInputEvent 方法中會調用 onInputEvent 方法進行事件的處理,InputEventReceiver 是抽象類,它的一個子類 WindowInputEventReceiver 是用來處理 input 事件的,WindowInputEventReceiver 是 frameworks/base/core/java/android/view/ViewRootImpl.java 的內部類,它覆寫了 onInputEvent 方法,該方法中會調用 enqueueInputEvent 方法對事件進行入隊。

webp-1720679507371-174

Java 層事件入口#
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    // 直接調用onInputEvent
    onInputEvent(event);
}
  
public void onInputEvent(InputEvent event) {
    // 直接調用finishInputEvent回收事件,所以此方法需要子類覆寫
    finishInputEvent(event, false);
}

dispatchInputEvent 方法是 jni 直接調用的方法,屬於 Java 層的入口方法,其中會直接調用 onInputEvent 方法,onInputEvent 方法中直接調用了 finishInputEvent 回收事件,所以就需要子類去實現具體的分發邏輯。

對事件進行兼容性處理#

onInputEvent 方法中首先調用 InputCompatProcessor 的 processInputEventForCompatibility 方法對事件進行兼容性處理,這個方法中會判斷應用的 targetSdkVersion 如果小於 M 並且是 motion 事件則進行兼容處理並返回,否則返回 null;然後會調用 enqueueInputEvent 方法對事件進行入隊。

@Override
public void onInputEvent(InputEvent event) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
    List<InputEvent> processedEvents;
    try {
        // 這塊兒主要是對低版本進行兼容性處理
        processedEvents =
            mInputCompatProcessor.processInputEventForCompatibility(event);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    if (processedEvents != null) {
        // 安卓M及以下版本,在這裡處理
        if (processedEvents.isEmpty()) {
            // InputEvent consumed by mInputCompatProcessor
            finishInputEvent(event, true);
        } else {
            for (int i = 0; i < processedEvents.size(); i++) {
                enqueueInputEvent(
                        processedEvents.get(i), this,
                        QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
            }
        }
    } else {
        // 這裡對事件進行入隊
        enqueueInputEvent(event, this, 0, true);
    }
}
  
public InputEventCompatProcessor(Context context) {
    mContext = context;
    // 獲取應用的targetsdk版本
    mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    mProcessedEvents = new ArrayList<>();
}
  
public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
    // 小於M並且是motion事件則進行兼容處理
    if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
        mProcessedEvents.clear();
        MotionEvent motion = (MotionEvent) e;
        final int mask =
                MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
        final int buttonState = motion.getButtonState();
        final int compatButtonState = (buttonState & mask) >> 4;
        if (compatButtonState != 0) {
            motion.setButtonState(buttonState | compatButtonState);
        }
        mProcessedEvents.add(motion);
        return mProcessedEvents;
    }
    return null;
}
轉變事件類型並加入隊列#

此方法主要是將 InputEvent 轉變為 QueuedInputEvent 並將其放到鏈表的末尾,然後調用 doProcessInputEvents 方法進行處理

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    // 轉變為QueuedInputEvent
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
  
    // 將事件插入到鏈表的末尾
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);
  
    if (processImmediately) {
        // 調用doProcessInputEvents繼續處理
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}
循環事件鏈並進行分發#

此方法中會遍歷整個事件鏈表,對每個事件調用 deliverInputEvent 方法進行分發

void doProcessInputEvents() {
    // 遍歷整個鏈表,對事件進行分發
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;
        // 省略若干行
        // 分發事件
        deliverInputEvent(q);
    }
}
將事件分發到 InputStage#

此方法中會根據 flag 獲取 InputStage,然後調用 InputStage 的 deliver 方法分發事件。這裡的 InputStage 後面會介紹到,它主要是用來將事件的處理分成多個階段進行。

private void deliverInputEvent(QueuedInputEvent q) {
    // 省略若干行
    try {
        // 省略若干行
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            // flag包含FLAG_UNHANDLED會走這裡
            stage = mSyntheticInputStage;
        } else {
            // 是否跳過輸入法窗口進行分發
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        // 省略若干行
        if (stage != null) {
            // 處理窗口焦點變更
            handleWindowFocusChanged();
            // 分發事件
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

InputStage#

InputStage 主要是用來將事件的處理分成若干個階段(stage)進行,事件依次經過每一個 stage,如果該事件沒有被處理(標識為 FLAG_FINISHED),則該 stage 就會調用 onProcess 方法處理,然後調用 forward 執行下一個 stage 的處理;如果該事件被標識為處理則直接調用 forward,執行下一個 stage 的處理,直到沒有下一個 stage(也就是最後一個 SyntheticInputStage)。這裡一共有 7 中 stage,各個 stage 間串聯起來,形成一個鏈表,各個 stage 的處理過程大致如下圖所示:

webp-1720679655803-177

先來看下這些 stage 是如何串起來的,所有的 Stage 都繼承於 InputStage,而 InputStage 是抽象類,它的定義如下:

abstract class InputStage {
    private final InputStage mNext;
    /**
        * Creates an input stage.
        * @param next The next stage to which events should be forwarded.
        */
    public InputStage(InputStage next) {
        // 從構成函數的定義能夠看到,傳入的next會賦值給目前實例的next,
        // 因此,先插入的就會是最後一個節點(頭插法),最終會形成一個鏈表
        mNext = next;
    }
}

在 ViewRootImpl 的 setView 方法中有以下代碼段:

// 如下創建出來的7個實例會串在一起形成一個鏈表,
// 鏈表的頭是最後創建出來的nativePreImeStage,
// 鏈表的尾是首先構造出來的mSyntheticInputStage
// Set up the input pipeline.
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
        "aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
// 輸入法對應的stage
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
        "aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
        "aq:native-pre-ime:" + counterSuffix);
// 第一个处理事件的stage为NativePreImeInputStage
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
分發傳遞事件#

該方法中會通過 flag 判斷事件是否已經處理,若已經處理則向前調用下一個 stage 處理事件(next 的 deliver 方法),否則會調用 onProcess 來處理事件(此方法需要子類實現),然後會根據處理的結果判斷是否需要調用鏈表中的下一個 stage 來繼續處理。

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        // 調用next的deliver方法繼續分發處理
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
            // 自身處理事件
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        // 判斷是否需要繼續分發處理
        apply(q, result);
    }
}
處理 input 事件#

這裡我們以 ViewPostImeInputStage 為例 (此 stage 會將事件傳遞到視圖層)介紹事件的分發過程,在 onProcess 方法中根據 event 的類型調用不同的方法進行分發。

@Override
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        // 處理key事件
        return processKeyEvent(q);
    } else {
        final int source = q.mEvent.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            // 處理pointer事件
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            // 處理軌跡球事件
            return processTrackballEvent(q);
        } else {
            // 處理一般的motion事件
            return processGenericMotionEvent(q);
        }
    }
}
處理 key 事件#

此方法中會調用 view 的 dispatchKeyEvent 方法來將 input 事件分發到 view 樹上,然後按照 view 的事件分發機制繼續分發處理事件,需要注意的是這裡的 mView 是 decorview 實例,在 ViewRootImpl 的 setView 方法中被設置。

private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;
    // 省略若干行
    // 調用view的dispatchKeyEvent來分發事件,此處的mView是decorview
    if (mView.dispatchKeyEvent(event)) {
        return FINISH_HANDLED;
    }
    // 省略若干行
    // 繼續調用鏈表的下一個stage來處理
    return FORWARD;
}

DecorView 處理事件#

mView 在 ViewRootImpl 的 setView 方法中被賦值,被賦值的對象便是 DecorView 的實例,而在 ViewPostImeInputStage 的 onPressess 方法中會將 key 事件通過 dispatchKeyEvent 方法傳遞到 DecorView 中。

dispatchKeyEvent#

此方法中先獲取 Window.Callback 對象,然後調用其 dispatchKeyEvent 繼續處理,若 callback 為 null 則調用父類同名方法來處理。最後,還會回調 window 的 onKeyDown 和 onKeyUp 方法,需要注意的是 Activity 和 Dialog 都默認實現了 Window.Callback 接口的方法,所以這裡就會將事件傳遞到 Activity 或者 Dialog 裡。

public boolean dispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    if (!mWindow.isDestroyed()) {
        // window沒有銷毀,如何存在Window.Callback,
        // 則調用callback的dispatchKeyEvent繼續處理
        // 否則調用父類的dispatchKeyEvent來處理
        final Window.Callback cb = mWindow.getCallback();
        final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                : super.dispatchKeyEvent(event);
        if (handled) {
            return true;
        }
    }
  
    // 這裡回調window的onKeyDown和onKeyUp方法
    return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
            : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}

Window.Callback(Activity)分發事件#

以 Activity 來介紹事件的傳遞流程,查看 Activity 的 dispatchKeyEvent 方法。

dispatchKeyEvent#
public boolean dispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    // 調用Window的superDispatchKeyEvent方法繼續處理
    Window win = getWindow();
    if (win.superDispatchKeyEvent(event)) {
        return true;
    }
    View decor = mDecor;
    if (decor == null) decor = win.getDecorView();
    // 此處調用KeyEvent的dispatch,傳入的receiver為當前實例,
    // 其內部會根據event的action來調用當前實例的onKeyDown和onKeyUp方法
    return event.dispatch(this, decor != null
            ? decor.getKeyDispatcherState() : null, this);
}

PhoneWindow 中處理事件#

Activity 中 getWindow 返回的其實是 PhoneWindow 的實例,看一下 PhoneWindow 的方法實現。

superDispatchKeyEvent#

此方法中會直接調用 mDecor 的同名方法,這個 mDecor 是 DecorView 的實例。

public boolean superDispatchKeyEvent(KeyEvent event) {
    // 調用DecorView的同名方法
    return mDecor.superDispatchKeyEvent(event);
}

DecorView 繼續分發事件到 View 樹#

這裡 Activity 通過調用 DecorView 的 superDispatchKeyEvent 方法,又將事件傳入到 DecorView 裡了,這裡和前面事件傳遞到 DecorView 的不同之處是前面傳遞到 dispatchKeyEvent,而這裡是 superDispatchKeyEvent。

superDispatchKeyEvent#

此方法中會直接調用 DecorView 的父類的 dispatchKeyEvent 方法,如果事件沒有被處理掉的話,就會通過 ViewRootImpl 將其作為 UnhandledEvent 進行處理。

public boolean superDispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    // 這裡直接調用父類的dispatchKeyEvent方法,DecorView的父類是FrameLayout,
    // 所以就分發到ViewGroup了,接下來就按照View樹的形式從根View向子View分發
    if (super.dispatchKeyEvent(event)) {
        return true;
    }
    // 如果沒有被消費掉,則通過ViewRootImpl將其作為Unhandled繼續處理
    return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
}

webp-1720680021901-180

Native 層傳遞過程#

Input 輸入處理流程 Native

InputEventReceiver 的事件來源於哪裡#

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    // 省略若干行
    for (;;) {
        // 省略若干行
        InputEvent* inputEvent;
        // 這裡調用了InputConsumer的consume方法來獲取輸入事件
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent,
                &motionEventType, &touchMoveNum, &flag);
        // 省略若干行
        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}

InputConsumer#

consume 方法中主要是從 InputChannel 獲取輸入事件的信息,然後根據消息中獲取的事件類型構造出對應的 event,並將消息中的事件信息賦值給 event 對象。

InputConsumer 處理事件#

從上面分析我們能夠看到,再 NativeInputEventReceiver 的 consumeEvents 方法中,會循環調用 InputConsumer 的 consume 方法獲取事件並進行處理。InputConsumer 的 consume 方法中會通過 InputChannel 從 socket 中通過 recv 系統調用獲取下層傳遞的事件,獲取到事件後就會通過 jni 向 Java 層傳遞。

webp-1720680518292-186

status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
                                nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
                                int* motionEventType, int* touchMoveNumber, bool* flag) {
    // 省略若干行
    *outSeq = 0;
    *outEvent = nullptr;
    // Fetch the next input message.
    // Loop until an event can be returned or no additional events are received.
    while (!*outEvent) {
        if (mMsgDeferred) {
            // mMsg contains a valid input message from the previous call to consume
            // that has not yet been processed.
            mMsgDeferred = false;
        } else {
            // Receive a fresh message.
            // 這裡通過調用InputChannel的receiveMessage來獲取消息
            status_t result = mChannel->receiveMessage(&mMsg);
            // 省略若干行
        }
        // 根據消息的類型生成不同的Event
        switch (mMsg.header.type) {
            case InputMessage::Type::KEY: {
                // 構造一個KeyEvent
                KeyEvent* keyEvent = factory->createKeyEvent();
                if (!keyEvent) return NO_MEMORY;
                // 從msg中獲取事件的各屬性,並賦值給構造出的Event對象
                initializeKeyEvent(keyEvent, &mMsg);
                *outSeq = mMsg.body.key.seq;
                *outEvent = keyEvent;
                if (DEBUG_TRANSPORT_ACTIONS) {
                    ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
                          mChannel->getName().c_str(), *outSeq);
                }
            break;
            }
  
            case InputMessage::Type::MOTION: {
                // 構造一個MotionEvent
                MotionEvent* motionEvent = factory->createMotionEvent();
                if (!motionEvent) return NO_MEMORY;
                updateTouchState(mMsg);
                // 從msg中獲取事件的各屬性,並賦值給構造出的Event對象
                initializeMotionEvent(motionEvent, &mMsg);
                *outSeq = mMsg.body.motion.seq;
                *outEvent = motionEvent;
  
                if (DEBUG_TRANSPORT_ACTIONS) {
                    ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
                          mChannel->getName().c_str(), *outSeq);
                }
                break;
            }
            // 省略若干行
  
        }
    }
    return OK;
}

這裡我們先看下 event 的構造和初始化,輸入消息的獲取隨後再介紹。先看下 factory->createMotionEvent,這裡 factory 是 PreallocatedInputEventFactory 的實例。

class PreallocatedInputEventFactory : public InputEventFactoryInterface {
public:
    PreallocatedInputEventFactory() { }
    virtual ~PreallocatedInputEventFactory() { }
    // 可以看到這裡返回的是全局變量的地址
    virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
    virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
    virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
  
private:
    // 這裡定義不同類型的事件變量
    KeyEvent mKeyEvent;
    MotionEvent mMotionEvent;
    FocusEvent mFocusEvent;
};

好了,我們繼續看 event 的初始化。這裡主要是從 msg 中獲取對應事件的詳細信息然後賦值給對應的 event 對象上。

// 對key事件進行初始化
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
    event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source,
                      msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action,
                      msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode,
                      msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime,
                      msg->body.key.eventTime);
}
  
// 對motion事件進行初始化
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
    // 省略若干行
    event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
                      msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
                      msg->body.motion.actionButton, msg->body.motion.flags,
                      msg->body.motion.edgeFlags, msg->body.motion.metaState,
                      msg->body.motion.buttonState, msg->body.motion.classification,
                      msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
                      msg->body.motion.yOffset, msg->body.motion.xPrecision,
                      msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
                      msg->body.motion.yCursorPosition, msg->body.motion.downTime,
                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
}

然後我們繼續看 msg 的獲取方法:InputChannel 的 receiveMessage 方法。

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        // 這裡通過recv系統調用從socket中讀取消息
        nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);
    // 省略若干行
    return OK;
}

可以看到這個方法主要是從 socket 中讀取消息。那這裡的 socket 是什麼時候建立的呢?我們繼續向下看。

InputConsumer 的構建#

在 NativeInputEventReceiver 的構造方法中,會創建出 NativeInputEventReceiver,並將 InputChannel 傳入。而 NativeInputEventReceiver 的構建是在 Java 層 InputEventReceiver 的 native 方法 nativeInit 中創建,並且能夠看到,這裡的 InputChannel 是從 Java 層傳遞下來的。

InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
        mResampleTouch(isTouchResamplingEnabled()),
        // 初始化InputChannel
        mChannel(channel), mMsgDeferred(false) {
}

我們發現在 InputConsumer 構造時對 InputChannel 進行了初始化,那就繼續超前看 InputConsumer 在哪裡構建的

NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<InputChannel>& inputChannel,
        const sp<MessageQueue>& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
    }
}

回到 NativeInputEventReceiver 中,發現它的構造方法中傳入了 InputChannel,那麼繼續看 NativeInputEventReceiver 的構建。

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    // 通過jni獲取java創建的InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    // 省略若干行
    // 構建出NativeInputEventReceiver
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    // 初始化Receiver
    status_t status = receiver->initialize();
    // 省略若干行
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

通過上述分析,我們發現 NativeInputEventReceiver 中獲取底層事件的 InputChannel 是來自於 Java 層的傳遞,那麼,InputChannel 又是如何創建的呢?

InputChannel#

InputChannel 會作為句柄傳遞到下層,後面分發事件的時候會通過它來進行。而且這裡會創建出兩個,一個作為 server 端註冊到 InputManagerService,最終會註冊到 InputDispatcher 中去,另一個則作為 client 端來接收 server 端的事件。

InputChannel 的創建#

通過前面分析,我們發現 NativeInputEventReceiver 中的 InputChanel 來源於 Java 層的 InputChannel。上述 nativeInit 是 Java 層 InputEventReceiver 的 native 方法,繼續看 Java 層的 InputEventReceiver。

webp-1720680669663-189

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    // 省略若干行
    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    // 將Java層的inputChannel向下層傳遞
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);
    mCloseGuard.open("dispose");
}

Java 層 InputEventReceiver 構造時傳入了 InputChannel。在 ViewRootImpl 的 setView 方法中會創建 InputChannel,然後會調用 Session 的 addToDisplayAsUser 方法初始化 InputChannel

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            // 省略若干行
            InputChannel inputChannel = null;
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                inputChannel = new InputChannel();
            }
            // 省略若干行
            // 調用Session的addToDisplayAsUser方法來添加window,
            // 會初始化InputChannel
            res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mDisplayCutout, inputChannel,
                    mTempInsets, mTempControls);
            // 省略若干行
            if (inputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                // 將InputChannel傳入InputEventReceiver
                mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                        Looper.myLooper());
            }
            // 省略若干行
        }
    }
}

Session 的 addToDisplayAsUser 方法會繼續調用 WindowManagerService 的 addWindow 方法。

public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, int userId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
    // 直接調用WindowManagerService的addWindow方法
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
            outInsetsState, outActiveControls, userId);
}

addWindow 方法中會調用 WindowState 打開 InputChannel。

public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
        int requestUserId) {
    // 省略若干行
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
            appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
            session.mCanAddInternalSystemWindow);
    // 省略若干行
    final boolean openInputChannels = (outInputChannel != null
            && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    if  (openInputChannels) {
        // 這裡會調用WindowState的openInputChannel來打開inputChannel
        win.openInputChannel(outInputChannel);
    }
    // 省略若干行
    return res;
}

繼續看 WindowState 的 openInputChannel 方法。首先會通過調用 InputChannel 的靜態方法 openInputChannelPair 來創建兩個 InputChannel,一個作為 client 一個作為 server;然後還會調用 InputManagerService 的 registerInputChannel 來註冊 server 端的 InputChannel;最後將 client 端的 InputChannel 設置到 outInputChannel 中。

void openInputChannel(InputChannel outInputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
    }
    String name = getName();
    // 通過openInputChannelPair方法創建出兩個InputChannel
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    // 註冊server端的InputChannel到InputManagerService中
    mWmService.mInputManager.registerInputChannel(mInputChannel);
    mInputWindowHandle.token = mInputChannel.getToken();
    if (outInputChannel != null) {
        // 將client端的InputChannel設置到outInputChannel
        mClientChannel.transferTo(outInputChannel);
        mClientChannel.dispose();
        mClientChannel = null;
    } else {
        // If the window died visible, we setup a dummy input channel, so that taps
        // can still detected by input monitor channel, and we can relaunch the app.
        // Create dummy event receiver that simply reports all events as handled.
        mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
    }
    mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}

上述 openInputChannelPair 方法中會直接調用 InputChannel 的 native 方法 nativeOpenInputChannelPair 來創建出一對 InputChannel。

public static InputChannel[] openInputChannelPair(String name) {
    if (name == null) {
        throw new IllegalArgumentException("name must not be null");
    }
  
    if (DEBUG) {
        Slog.d(TAG, "Opening input channel pair '" + name + "'");
    }
    // 繼續調用natvie方法創建出兩個InputChannel
    return nativeOpenInputChannelPair(name);
}

jni 方法 nativeOpenInputChannelPair 中會繼續調用 InputChannel 的 openInputChannelPair 靜態方法。然後將創建出的兩個 inputChannel 分別添加到數組中,然後返回給上層。

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    ScopedUtfChars nameChars(env, nameObj);
    std::string name = nameChars.c_str();
  
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    // 創建出server端和client端的InputChannel
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    // 省略若干行
    // 添加到數組中,然後返回給上層
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

openInputChannelPair 方法中會首先通過 socketpair 創建一對相互連接的套接字,然後分別給 socket 設置相應的選項值;然後通過 InputChannel 的 create 方法創建出兩個分別與 socket 關聯的 inuptChannel。

status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    // 創建一對相互連接的socket
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        // 創建失敗做相應的處理
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.c_str(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }
  
    // 分別設置兩個socket的可讀可寫buffer
    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
  
    sp<IBinder> token = new BBinder();
  
    std::string serverChannelName = name + " (server)";
    android::base::unique_fd serverFd(sockets[0]);
    // 創建出server端InputChannel,並於socket關聯
    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
  
    std::string clientChannelName = name + " (client)";
    android::base::unique_fd clientFd(sockets[1]);
    // 創建出client端InputChannel,並於socket關聯
    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
    return OK;
}

至此,InputChannel 便創建並關聯上 socket 上了。並且通過前面的介紹,我們知道了獲取輸入事件時是從 client 端的 socket 中讀取消息並進行事件封裝,然後傳遞到上層。但是這裡我們發現有一個問題,就是 client 端 socket 中的數據是從哪裡來的呢?我們繼續看一下 WindowState 的 openInputChannel 方法。

server 端 InputChannel 的註冊#

在通過 openInputChannel 開啟 InputChannel 後,會調用 InputManagerService 的 registerInputChannel 方法註冊 server 端的 InputChannel

webp-1720680863560-192

void openInputChannel(InputChannel outInputChannel) {
    // 省略若干行
    // 通過openInputChannelPair方法創建出兩個InputChannel
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    // 註冊server端的InputChannel到InputManagerService中
    mWmService.mInputManager.registerInputChannel(mInputChannel);
    // 省略若干行
}

我們發現 server 端的 InputChannel 被註冊到了 InputManagerService 中,那麼,我們繼續向下看。

public void registerInputChannel(InputChannel inputChannel) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null.");
    }
    // 調用native方法繼續註冊
    nativeRegisterInputChannel(mPtr, inputChannel);
}

在 InputManagerService 的 registerInputChannel 方法中直接調用了 native 方法 nativeRegisterInputChannel,我們繼續。

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 獲取InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == nullptr) {
        throwInputChannelNotInitialized(env);
        return;
    }
    // 將inputChannel註冊到NativeInputManager中
    status_t status = im->registerInputChannel(env, inputChannel);
    // 設置dispose的callback,在inputChannel
    // dispose之後會調用函數指針handleInputChannelDisposed
    // 來調用NativeInputManager的unregisterInputChannel
    // 解註冊inputChannel
    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
            handleInputChannelDisposed, im);
}

在 native 方法中,先調用 NativeInputManager 的 registerInputChannel 方法註冊 inputChannel,然後會給 inputChannel 設置 dispose callback,並且 callback 中執行了 inputChannel 的解註冊。在 NativeInputManager 的 registerInputChannel 方法中,會獲取 InputDispatcher,並將 inputChannel 註冊到其中去。

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}

在 InputDispatcher 的 registerInputChannel 方法中,會通過 InputChannel 構建出 Connection,然後將其添加到註冊列表當中。

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif
  
    { // acquire lock
        std::scoped_lock _l(mLock);
        // 省略若干行
        // 創建connection並添加的註冊列表中
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
        int fd = inputChannel->getFd();
        mConnectionsByFd[fd] = connection;
        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
        // 將inputChannel的fd添加到looper中,並且對應的event是ALOOPER_EVENT_INPUT
        // 傳入的looper callback為handleReceiveCallback方法,
        // 因此當事件到來時,會觸發此callback
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
  
    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

到這裡我們就知道了,server 端的 inputChannel 最終被註冊到了 InputDispatcher 的註冊列表中去了,所以 InputDispatcher 中就可以通過向 server 端的 socket 中寫入消息,然後 client 端就可以讀取到了。但是,這裡還發現存在一個問題:那就是 server 端寫入事件消息後,怎麼通知到 client 去開始處理呢?我們在回過頭來看一下前面介紹的 InputEventReceiver 的構造函數。

client 端 InputChannel 讀取事件並傳遞#

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    // 省略若干行
    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    // 將Java層的inputChannel向下層傳遞
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);
    mCloseGuard.open("dispose");
}

在 InputEventReceiver 的構造方法中調用了 native 方法 nativeInit 進行 native 層的初始化

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    // 省略若干行
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    // 初始化Receiver
    status_t status = receiver->initialize();
    // 省略若干行
    return reinterpret_cast<jlong>(receiver.get());
}

在 NativeInputEventReceiver 初始化時,會將 inputChannel 的文件描述符 fd 添加到 looper 中去,並且添加了 looper callback 為 NativeInputEventReceiver 實例自身,所以,當 server 端寫入事件消息時,就會觸發 callback,於是便調用到 NativeInputEventReceiver 的 handleEvent 方法。

status_t NativeInputEventReceiver::initialize() {
    // 設置文件描述符對應的event為ALOOPER_EVENT_INPUT
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
  
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    // 省略若干行
    // 接收添加的ALOOPER_EVENT_INPUT事件
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        // 調用consumeEvents方法處理事件
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    // 省略若干行
    return 1;
}

InputManagerService#

InputManagerService 簡稱 IMS,和其他系統服務一樣,是在 SystemServer 中創建並啟動,它主要是用來監測和加工輸入事件,並向上層傳遞。而且,上文所說的 InputDispatcher 以及 InputReader 均是在 InputManagerService 中構建出來的。

IMS 的創建#

在 SystemServer 的 startOtherServices 方法中,直接通過 new 的方式創建出 IMS 實例。

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    // 省略若干行
    t.traceBegin("StartInputManagerService");
    // 創建IMS
    inputManager = new InputManagerService(context);
    t.traceEnd();
    // 省略若干行
}

InputManagerService 的構造方法中,會先創建出 Handler,然後通過 native 方法 nativeInit 來實現 IMS 的初始化,主要是構建 native 層的 IMS。

public InputManagerService(Context context) {
    this.mContext = context;
    // 創建出handler
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    // 省略若干行
    // 調用native方法來構建native層IMS
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    // 省略若干行
}

native 方法中先獲取到上層傳下來的 messageQueue,然後獲取對應的 Looper,並構建出 NativeInputManager。

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    // 獲取上層傳遞的MessageQueue
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    // 構建NativeInputManager
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

NativeInputManager 構造中,先創建出 native 層 IMS 實例,然後將其添加到 serviceManager 中。

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    // 省略若干行
    // 構建出native層的IMS,即InputManager
    mInputManager = new InputManager(this, this);
    // 將IMS添加到serviceManager中
    defaultServiceManager()->addService(String16("inputflinger"),
            mInputManager, false);
}

在 InputManager 構建時,會分別創建出 InputDispatcher、InputListener 以及 InputReader 實例。這裡將 InputDispatcher 作為 InputListener 傳遞到 InputClassifier,並最終傳遞到 InputReader 中去。

InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    mReader = createInputReader(readerPolicy, mClassifier);
}

我們發現在 IMS 創建的最後,會創建出 InputDispatcher 和 InputReader,InputDispatcher 我們前面已經介紹了,主要是用於分發事件;而 InputReader 是用來獲取底層輸入事件的

IMS 的啟動#

我們繼續來看 SystemServer 的 startCoreServices 方法,在創建出 IMS 實例後,會調用其的 start 方法來啟動服務。

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    // 省略若干行
    t.traceBegin("StartInputManager");
    // 將WindowCallback傳遞給IMS
    inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
    // 調用start啟動服務
    inputManager.start();
    t.traceEnd();
    // 省略若干行
}

start 方法中會直接調用 native 的 nativeStart 方法來啟動 native 層的 IMS。

public void start() {
    Slog.i(TAG, "Starting input manager");
    // 調用native方法來啟動底層IMS
    nativeStart(mPtr);
    // 省略若干行
}

nativeStart 方法中會獲取到 InputManager,然後調用它的 start 方法。

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 調用InputManager的start方法
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

在 InputManager 的 start 方法中,先調用 InputDispatcher 的 start 方法來啟動 InputDispatcher,然後調用 InputReader 的 start 方法啟動 InputReader。

status_t InputManager::start() {
    status_t result = mDispatcher->start();
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
  
    result = mReader->start();
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);
  
        mDispatcher->stop();
        return result;
    }
  
    return OK;
}

InputDispatcher#

通過上述分析,知道了 InputDispatcher 的創建是在 IMS 創建時創建,那麼它是如何啟動起來的呢?我們繼續看 InputDispatcher 的 start 方法。

InputDispatcher 啟動#

在 InputDispatcher 的 start 方法中,會創建出 InputThread 線程,並傳入了兩個函數指針:dispatchOnce 以及 mLooper->wake。

status_t InputDispatcher::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    // 直接構造出Thread,傳入兩個回調函數
    mThread = std::make_unique<InputThread>(
            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
    return OK;
}

繼續看 InputThread 的構造過程,發現初始化列表中對傳入的回調函數進行了保存,然後構建 InputThreadImpl 並調用其 run 方法將線程啟動起來。

InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
    // 這裡保存wake回調函數
      : mName(name), mThreadWake(wake) {
    // 將loop函數傳入InputThreadImpl
    mThread = new InputThreadImpl(loop);
    mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}
  
InputThread::~InputThread() {
    mThread->requestExit();
    // 調用wake函數
    if (mThreadWake) {
        mThreadWake();
    }
    mThread->requestExitAndWait();
}
  
class InputThreadImpl : public Thread {
public:
    explicit InputThreadImpl(std::function<void()> loop)
                                            // 保存loop函數
          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
  
    ~InputThreadImpl() {}
  
private:
    std::function<void()> mThreadLoop;
  
    bool threadLoop() override {
        // 在線程的loop循環中調用傳入的loop函數。
        mThreadLoop();
        // 返回true線程會一直運行,直到requestExit被調用時退出
        return true;
    }
};

通過以上分析,我們發現 InputThread 構造時會創建出線程並將其啟動起來,傳入的 loop 函數(dispatchOnce)最終會作為線程的 loop 來執行,而 wake 函數(mLooper->wake)也會在線程析構時調用。

InputDispatcher 分發事件#

通過前面的介紹,我們了解到在 InputDispatcher 啟動時創建了線程,並且將 dispatchOnce 作為線程的執行函數傳入到 InputThread 中。所以,當 InputDispatcher 線程被喚醒後就會執行 dispatchOnce 方法來分發事件。

webp-1720681277193-195

在 dispatchOnce 方法中,首先會判斷是否有 command 需要處理(如:configChanged,focusChanged 等),如果有就會調用 runCommandsLockedInterruptible 方法執行所有 command,然後會再次觸發 wake 執行事件的處理;如果沒有則直接調用 dispatchOnceInnerLocked 來處理輸入事件;最後,looper 會再次進入睡眠等待下一次喚醒。

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();
  
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            // 這裡沒有command需要處理,就開始分發事件
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
  
        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        // 處理command,並修改nextWakeupTime
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
  
        // 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.
        // 檢測是否事件分發出現anr
        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);
    // 處理完成後調用looper的pollOnce進入睡眠狀態,等待下一次喚醒,
    // 如果是處理了command,則這個timeoutMillis為0
    // 所以會接著執行一次loop
    mLooper->pollOnce(timeoutMillis);
}

dispatchOnceInnerLocked 方法會先判斷是否有待分發的事件,沒有則從事件隊列中取出一個事件;然後根據事件不同的 type 調用不同的 dispatch 方法進行事件分發。

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    // 省略若干行
    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (!mPendingEvent) {
        // 如果沒有待分發的事件
        if (mInboundQueue.empty()) {
            // 省略若干行
            // Nothing to do if there is no pending event.
            // 事件隊列為空,並且沒有待分發的事件,直接返回
            if (!mPendingEvent) {
                return;
            }
        } else {
            // Inbound queue has at least one entry.
            // 從隊列中取出一個事件
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }
        // 省略若干行
    switch (mPendingEvent->type) {
        // 省略若干行
        // 根據事件的type分別進行處理
        case EventEntry::Type::KEY: {
            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
            // 省略若干行
            // 分發key事件
            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
  
        case EventEntry::Type::MOTION: {
            // 省略若干行
            // 分發motion事件
            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }
}

這裡以 key 事件為例繼續介紹,dispatchKeyLocked 中首先通過 findFocusedWindowTargetsLocked 方法查找到焦點窗口,然後調用 dispatchEventLocked 朝焦點窗口上分發事件。

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // 省略若干行
    // Identify targets.
    std::vector<InputTarget> inputTargets;
    // 查找focus window
    int32_t injectionResult =
            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    // 省略若干行
    // 將事件分發到對應window上去
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

dispatchEventLocked 方法中會遍歷所有查找到的 focus 窗口(inputTarget),然後通過 inputChannel 獲取到鏈接對象 connection,最後通過 prepareDispatchCycleLocked 將事件分發出去。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
    // 省略若干行
    // 遍歷所有的inputTarget
    for (const InputTarget& inputTarget : inputTargets) {
        // 通過inputChannel獲取connection
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            // 開始事件分發
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        }
        // 省略若干行
    }
}

prepareDispatchCycleLocked 方法中先判斷是否需要 split motion 事件並進行處理,最後調用 enqueueDispatchEntriesLocked 方法將待分發的事件添加到 mOutboundQueue 隊列中。

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection,
                                                 EventEntry* eventEntry,
                                                 const InputTarget& inputTarget) {
    // 省略若干行
    // Split a motion event if needed.
    // 如果需要split motion事件則進行處理
    if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
        LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
                            "Entry type %s should not have FLAG_SPLIT",
                            EventEntry::typeToString(eventEntry->type));
  
        const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
        if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
            // 省略若干行
            enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
            splitMotionEntry->release();
            return;
        }
    }
  
    // Not splitting.  Enqueue dispatch entries for the event as is.
    // 將將要分發的事件入隊
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

enqueueDispatchEntriesLocked 方法中會分別處理不同的 flag 對應的事件將其添加到 outboundQueue 中,最後通過 startDispatchCycleLocked 開始事件的分發。

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   EventEntry* eventEntry,
                                                   const InputTarget& inputTarget) {
    // 省略若干行
  
    bool wasEmpty = connection->outboundQueue.empty();
    // 分別處理不同flag對應的event,並將其添加到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()) {
        // 如果有event被添加到隊列則開始處理
        startDispatchCycleLocked(currentTime, connection);
    }
}

startDispatchCycleLocked 方法中通過遍歷 outboundQueue 隊列,取出所有的 event,然後根據其 type 分別調用 InputPublisher 的 publishXxxEvent 將事件分發出去。

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
    // 省略若干行
    // 循環遍歷outboundQueue隊列
    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
        // 從outboundQueue隊列取出事件
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        dispatchEntry->deliveryTime = currentTime;
        const nsecs_t timeout =
                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
        dispatchEntry->timeoutTime = currentTime + timeout;
  
        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        // 根據event的不同type分別進行分發
        switch (eventEntry->type) {
            case EventEntry::Type::KEY: {
                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
  
                // Publish the key event.
                // 分發key事件
                status =
                        connection->inputPublisher
                                .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
                                                 keyEntry->deviceId, keyEntry->source,
                                                 keyEntry->displayId, std::move(hmac),
                                                 dispatchEntry->resolvedAction,
                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                 keyEntry->scanCode, keyEntry->metaState,
                                                 keyEntry->repeatCount, keyEntry->downTime,
                                                 keyEntry->eventTime);
                break;
            }
  
            case EventEntry::Type::MOTION: {
                MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
  
                PointerCoords scaledCoords[MAX_POINTERS];
                const PointerCoords* usingCoords = motionEntry->pointerCoords;
                // 省略若干行
                // 分發motion事件
                // Publish the motion event.
                status = connection->inputPublisher
                                 .publishMotionEvent(dispatchEntry->seq,
                                                     dispatchEntry->resolvedEventId,
                                                     motionEntry->deviceId, motionEntry->source,
                                                     motionEntry->displayId, std::move(hmac),
                                                     dispatchEntry->resolvedAction,
                                                     motionEntry->actionButton,
                                                     dispatchEntry->resolvedFlags,
                                                     motionEntry->edgeFlags, motionEntry->metaState,
                                                     motionEntry->buttonState,
                                                     motionEntry->classification, xScale, yScale,
                                                     xOffset, yOffset, motionEntry->xPrecision,
                                                     motionEntry->yPrecision,
                                                     motionEntry->xCursorPosition,
                                                     motionEntry->yCursorPosition,
                                                     motionEntry->downTime, motionEntry->eventTime,
                                                     motionEntry->pointerCount,
                                                     motionEntry->pointerProperties, usingCoords);
                reportTouchEventForStatistics(*motionEntry);
                break;
            }
        // 省略若干行
    }
}

這裡以 key 事件為例繼續介紹,在 publishKeyEvent 方法中,首先會根據傳入的 event 詳細信息構建出 InputMessage,然後再調用 InputChannel 的 sendMessage 方法將 msg 發送出去。

status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                         int32_t source, int32_t displayId,
                                         std::array<uint8_t, 32> hmac, int32_t action,
                                         int32_t flags, int32_t keyCode, int32_t scanCode,
                                         int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                         nsecs_t eventTime) {
    // 省略若干行
    // 根據event信息構建InputMessage
    InputMessage msg;
    msg.header.type = InputMessage::Type::KEY;
    msg.body.key.seq = seq;
    msg.body.key.eventId = eventId;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.displayId = displayId;
    msg.body.key.hmac = std::move(hmac);
    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;
    // 通過InputChannel的sendMessage方法將event發送出去
    return mChannel->sendMessage(&msg);
}

sendMessage 主要就是先 copy 一份事件 msg,然後調用 send 將 msg 循環寫入 socket,從而實現輸入事件的分發。

status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    // copy一份msg
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        // 通過socket循環寫入msg
        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    // 省略若干行
    return OK;
}

InputReader(InputDispatcher 事件的來源)#

InputDispatcher 中的事件是從 InputReader 中來的,InputReader 從 EventHub 中獲取到輸入事件後,會通過調用 InputDispatcher 的 notifyXxx 方法來將事件傳遞到 InuptDispatcher 中。

InputReader 啟動#

在 IMS 的 start 方法中會調用 InputReader 的 start 方法來啟動 InputReader,我們繼續看 InputReader 的 start 方法。在 start 方法中,會創建出 InputThread 線程,這裡注意,創建線程時傳入了兩個函數指針(laumda 表達式):loopOnce 和 mEventHub->wake。通過上對 InputThread 的介紹,我們知道最終,loopOnce 會作為線程的循環方法進行調用,而 mEventHub->wake 最終也會在線程析構時觸發。

status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    // 直接構造出Thread,傳入兩個回調函數
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

InputReader 處理事件#

InputReader 在其線程的 threadLoop 中會調用 loopOnce 從 EventHub 中獲取輸入事件,如果獲取到事件,則繼續調用 processEventsLocked 進行處理。接著會調用 InputDevice -> InputMapper -> InputDispatcher (InputListenerInterface),在 InputDispatcher 中觸發 notifyXxx 方法,從而將事件分發出去。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。