天天看點

Android Framework 輸入子系統(05)InputDispatcher解讀

該系列文章總綱連結:專題分綱目錄 Android Framework 輸入子系統

本章關鍵點總結 & 說明:

Android Framework 輸入子系統(05)InputDispatcher解讀

這裡思維導圖主要關注➕派發流程 以及啟動流程的按鍵視角。因為圖比較大,是以這裡單獨把新增的部分單獨截圖,IMS的架構部分新增:

Android Framework 輸入子系統(05)InputDispatcher解讀

這裡新增加了 按鍵視角部分,以按鍵的流程來解讀整個IMS的資料流走向的架構。新增的派發資料流程截圖如下:

Android Framework 輸入子系統(05)InputDispatcher解讀

這裡整個InputSispatcher的分析流程就如上思維導圖所示。

1 Input子系統架構

這裡 步驟1和2的目的是獲得事件,步驟3和4的目的是稍加處理,步驟5 目的是為了發送給APP。接下來用一張架構圖來表示InputDispatcher的整體架構,如下所示:

Android Framework 輸入子系統(05)InputDispatcher解讀

以上架構圖以圖形的方式表達了Input輸入子系統中關鍵的五步,如下所示:

  1. 在放入隊列前先稍加處理,主要針對System Key、Global Key、User Key事件,以及處理緊急事件(比如來電靜音等)。
  2. 把InputReader線程中事件放入mInboundQueue中即可。
  3. 對于Global Key、Sysytem Key按鍵處理,放入mCommandQueue,依次處理。
  4. 對于User Key,放入隊列,查找目标APP,并放入它的outBoundQueue。
  5. 從OutboundQueue取出 事件,通過connection發送給APP。

對于InputDispatcher線程,更關注3 、4、5步。

2 Dispatcher對按鍵的處理流程關鍵點解析

2.1 通用模型的處理流程

回顧之前key的處理流程,關鍵點在于這段代碼:

//InputReader的分析:KeyboardInputMapper::processKey中資料最後同步到dispatcher中流程:
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
	//說明:這裡getListener是InputReader初始化時傳入的對象,即InputDispatcher
    getListener()->notifyKey(&args);           

這裡繼續分析notifyKey中的關鍵點,如下所示:

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    //...
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
	//...
    needWake = enqueueInboundEventLocked(newEntry);
    //...
    if (needWake) {
        //喚醒等待的InputDispatcher,進行輸入事件分發。
        mLooper->wake();
    }
}           

這裡關注➕三個主要步驟:

  1. interceptKeyBeforeQueueing函數,調用後對應PhoneWindowManager的同名函數處理,根據傳回值設定PolicyFlags
  2. enqueueInboundEventLocked函數,将newEntry放入到mInBoundQueue隊列
  3. mLooper->wake();根據需要喚醒Dispatcher線程

2.2 案例簡要說明

@1 針對Global Key的處理模式

這裡以按鍵AKEY_CODE_TV為例,對于Global Key,直接傳回ACTION_PASS_TO_USER。

@2 針對System Key的處理模式

這裡以按鍵AKEY_CODE_VOLUME_DOWN為例,對于System Key,如果可以直接處理則設定傳回值~ACTION_PASS_TO_USER,否則設定ACTION_PASS_TO_USER。

@3 針對User Key的處理模式

這裡以按鍵AKEY_CODE_A為例,對于普通按鍵則直接傳回ACTION_PASS_TO_USER。

3 接下來是對DispatcherThread的分析,實際流程如下:

這裡從InputManager的start函數,延續之前 章節的分析,代碼如下:

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    //...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    //...
    return OK;
}           

InputManager啟動了一個InputReaderThread和InputDispatcherThread來讀取和分發輸入消息,調用它們的run方法後,會進入threadLoop函數(隻要threadLoop函數傳回true,該函數就會循環執行)。是以接下來直接分析InputDispatcherThread的threadLoop函數,代碼實作如下:

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}           

3.1 dispatchOnce分析

其中 dispatchOnce代碼實作如下:

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();
        
        /* 指令隊列為空時
        @a 從mInboundQueue中取出事件
        @b 用它來生成指令放入指令隊列 or 直接丢棄
        @d 對于經過處理的事件:
          Global Key:丢棄
          System Key:丢棄
          User Key:找到target,執行dispatch*/
        if (!haveCommandsLocked()) {//判斷CommandQueue是否有指令
            dispatchOnceInnerLocked(&nextWakeupTime);//關鍵點
        }
        /*@c 當指令隊列有資料時,執行指令:
          Global Key:發廣播
          System Key:直接處理
          User Key:不處理*/
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}           

@1 這裡關注runCommandsLockedInterruptible函數,實作如下:

bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }
    do {
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
        Command command = commandEntry->command;
        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());
    return true;
}           

以上是将 指令 放入指令隊列裡的過程,後面将會繼續執行指令。

@2 一次輸入事件分發,當沒有事件輸入消息時會走到mLooper->pollOnce(timeoutMillis);這個函數會進入睡眠狀态。

當有按鍵消息發生時該函數會傳回,然後走到dispatchOnceInnerLocked函數。代碼實作如下:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    //...
    // 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;
    }
    //...
    }
    //...
}           

上面是以按鍵為例,是以派發按鍵事件時僅關注 按鍵部分,繼續分析dispatchKeyLocked,之前在InputReaderThread線程中調用enqueueInboundEventLocked函數,将EventEntry加入到mInboundQueue中,然後調用mLooper->wake函數會喚醒InputDispatcherThread線程,InputDispatcher中把隊列的第一個事件取出來,因為這裡是鍵盤事件,是以mPendingEvent->type是EventEntry::TYPE_KEY,然後調用dispatchKeyLocked函數。

3.2 按鍵案例

選擇 dispatchKeyLocked繼續分析,代碼實作如下:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    //...
    //如果按鍵是第一次分發,則将指令封裝為CommandEntry加入隊列
    //執行doInterceptKeyBeforeDispatchingLockedInterruptible,以給java層攔截按鍵的機會 
    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;
    //找到目前激活的Window視窗,并将其加入到Vendor中
    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;
    }
    //找到需要監聽按鍵的InputChannel,封裝成InputTarget,加入到Vendor中 
    addMonitoringTargetsLocked(inputTargets);

    //将按鍵分發到上面Vendor的InputChannel中
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}           

@1 這裡關注doInterceptKeyBeforeDispatchingLockedInterruptible的實作,代碼如下:

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;

    KeyEvent event;
    initializeKeyEvent(&event, entry);

    mLock.unlock();

    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);

    mLock.lock();

    if (delay < 0) {
        //不會上傳給APP,如果是system Key則直接處理,傳回-1
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        //會上傳給APP,讓APP處理,傳回0
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        //杜宇User Key 會再次執行前面的dispatcherOnceInnerLocked
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry->interceptKeyWakeupTime = now() + delay;
    }
    entry->release();
}           

這裡調用interceptKeyBeforeDispatching,會調用到PhoneWindowManager.java,如果這裡是針對Global Key,則代碼如下:

public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
        final boolean keyguardOn = keyguardOn();
        final int keyCode = event.getKeyCode();
        final int repeatCount = event.getRepeatCount();
        final int metaState = event.getMetaState();
        //...
        //根據Global Key.xml發送廣播給元件
        if (isValidGlobalKey(keyCode)
                && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
            return -1;
        }
        //...
}           

@2 同時 這裡繼續分析dispatchEventLocked,代碼實作如下:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    pokeUserActivityLocked(eventEntry);

    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函數進行分發。prepareDispatchCycleLocked代碼實作如下:

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    //...各種檢查
	//這裡将connection分裝成DispatchEntry,加入到connection->outboundQueue的隊列中
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}           

enqueueDispatchEntriesLocked的代碼實作如下:

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中。

  1. 如果以前為空,現在不為空,則調用startDispatchCycleLocked開始分發。
  2. 如果以前不為空,說明目前的Activity正在處理前面的按鍵,則不需要再調用startDispatchCycleLocked。

這裡 startDispatchCycleLocked的代碼實作如下:

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.

        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }
}           

從outboundQueue中取出需要處理的事件,交給connection的inputPublisher去分發,将事件加入到connection的waitQueue中。同時這裡 分發事件是通過InputPublisher的publishKeyEvent來完成,代碼如下:

status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        //...
        nsecs_t downTime,
        nsecs_t eventTime) {
	//...
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
	//...
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}           
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);
	//...
    if (size_t(nWrite) != msgLength) {
        return DEAD_OBJECT;
    }
    return OK;
}           
  1. 查找目标:向WindowManagerService查詢目前window,獲得對應的connection
  2. 把事件放入到connection的outboundQueue隊列當中
  3. 從隊列中逐個把事件寫入fd,取出并構造InputMessage最後發送。