按鍵事件在native和jni中的流程

按鍵事件在activity中的流程一文已經簡單介紹了按鍵事件在activity中的處理流程。本文則着重介紹事件進入activity之前的nativie和jni層的流程。

Native層的流程
native層相關類都在/frameworks/base/services/input目錄下,InputManager、InputReader、InputDispatcher、EventHub是幾個主要類。InputManager負責初始化的一些工作。在其構造方法裏,會創建InputReader、InputDispatcher、EventHub三個對象,並創建相應的reader和dispatcher線程。在InputManager的start()和stop()方法裏,會分別啓動和停止這2個線程。

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();  // 初始化線程
}

InputReaderThread線程會循環調用InputReader的loopOnec()方法,而該方法會先從EventHub的getEvent()函數獲取到當前InputDevice的事件,然後在processEventsLocked()中處理這些事件,最終事件會傳遞到InputDevice的process()函數中。

void InputReader::loopOnce() {
    // ... 一些本地變量
    { // acquire lock
      // config change和timeout的一些邏輯
    } // release lock

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); // 讀取事件並存入mEventBuffer

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) { // 事件處理
            processEventsLocked(mEventBuffer, count);
        }

        // timeout和device change的一些邏輯
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

    // Flush queued events out to the listener.
    mQueuedListener->flush();
}

每一個InputDevice對應一種輸入設備,而每一種輸入設備都有自己的InputMapper列表。InputMapper的process()函數將RawEvent轉換成key、touch等事件。按鍵事件對應的是KeyboardInputMapper。在其process()函數中,會先底層按鍵根據其key map layout轉換爲java層定義的按鍵值,然後將處理過的事件轉換爲NotifyKeyArgs,並存入一個Vector,通知到InputListener的notifyKey()函數。而InputReader裏的InputListener正是在其構造方法裏傳入的InputDispatcher,即事件從InputReader裏傳遞到了InputDispatcher中。

下面再來看InputDispatcher的notifyKey()函數。

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    // 檢查key action是否有效...
    // 設置policy flag和meta state ...
    KeyEvent event;
    // 初始化key event
    event.initialize(args->deviceId, args->source, args->action,
            flags, args->keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);

    // mPolicy是在InputDispatcher構造方法中傳入
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

    if (policyFlags & POLICY_FLAG_WOKE_HERE) {
        flags |= AKEY_EVENT_FLAG_WOKE_HERE;
    }

    bool needWake;
    { // acquire lock
        mLock.lock();

        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        int32_t repeatCount = 0;
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, args->keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

代碼中可以看到調用了mPolicy的interceptKeyBeforeQueueing()和filterInputEvent()函數,並將KeyEvent轉換爲了KeyEntry,存入事件隊列中。在InputDispatcherThread中,InputDispatcher的dispatchOnce()函數會被循環調用。而在dispatchOnce()函數中,會先將事件隊列中的事件一個個取出(第一個事件會先與PowerManagerService交互),找到當前焦點window作爲target,使用command模式,生成command並放入command隊列中,並在隨後的循環中,依次執行這些command。中間會調用到mPolicy的interceptKeyBeforeDispatching(),和InputDispatcher的dispatchEventLocked()函數。而事件將最終轉化爲DispatchEntry存入當前focus window對應的Connection的outboundQueue中,並在InputDispatcher的startDispatchCycleLocked()函數中依次被取出並通過inputPublisher的publishKeyEvent()函數分發出去。這個inputPublisher是在/frameworks/base/libs/androidfw/InputTransport.h中定義,而實現則在/frameworks/base/libs/androidfw/InputTransport.cpp中。在InputTransport.h中分別定義了InputChannel、InputPublisher、InputConsumer三個接口。InputPublisher作爲生產者,被InputDispatcher主動調用,將事件(key/touch)轉發給上層應用;InputConsumer作爲消費者,被上層應用主動調用,將事件(channel切換等)傳給InputDispatcher(抱歉,底層c++代碼看的並不是太明白,有錯誤請指出);而InputChannel使用管道做進程間通訊,提供上層和底層交互的機制。

這一段代碼邏輯太複雜,跨度太大,就不一一貼出來了,只說明主要的流程。

疑問:
InputDispatcher由InputManager初始化,而InputManager又是由誰初始化呢?InputChannel最終又將事件傳遞到了哪裏呢?

jni層的流程
jni層的代碼在/frameworks/base/services/jni中,主要是幾個以com_android_server_input_ 開頭的cpp文件。我們首先看com_android_server_input_InputManagerService.cpp。看名字就可以知道,它是InputManagerService.java類(輸入相關的系統服務)對應的jni接口。其核心類是NativeInputManager,而這個類實現了InputReaderPolicyInterface和InputDispatcherPolicyInterface接口。在NativeInputManager的構造方法中,就以自己爲參數直接創建了InputManager對象,並最終將自己做爲policy的實現傳遞給了InputReader和InputDispatcher。即上面最後一段代碼片段中看到的mPolicy就是這個NativeInputManager。NativeInputManager的interceptKeyBeforeQueueing()和filterInputEvent()都是簡單將native事件轉換爲java層的對應的事件對象,然後通過jni接口直接調用到了InputManagerService.java的同名函數中。另外在interceptKeyBeforeQueueing()函數中,還和PowerManagerService有一些簡單交互。

在上層的WindowManagerService中的addWindow()方法中,會調用InputChannel的openInputChannelPair()函數,最終這個InputChannel會保存在WindownState中。而java層的InputChannel和native的InputChannel是對應的。

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
            // ....

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);

                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }
        // ...
}

注意上邊的InputManagerService的registerInputChannel()函數。該方法會調用native層的com_android_server_input_InputManagerService.cpp中的同名方法,並最終調用到InputDispatcher的同名方法。而InputDispatcher在這裏會將InputChannel保存在Connection裏。在InputDispatcher的startDispatchCycleLocked()方法中的事件最終都會通過這個InputChannel傳到上層(抱歉,上層的InputEventReceiver如何和這個InputChannel對接並未弄明白)。最終在上層的InputEventReceiver的onInputEvent()函數中接收到這些事件。而InputEventReceiver的實現是在ViewRootImpl.java中,onInputEvent()回調中會調用ViewRootImpl的enqueueInputEvent()函數,最終進入PhoneWindow的內部類的dispatchKeyEvent()函數。如果事件未被處理,ViewRootImpl中會對方向鍵做查找焦點。如果不想使用默認的InputEventReceiver實現,還有一種方式是調用Window的takeInputQueue(InputQueue.Callback)函數,通過callback可以拿到所有的事件,自己做處理。細節邏輯可以查看ViewRootImpl的setView()函數。

疑問:
InputDispatcher的事件如何通過InputChannel傳給InputEventReceiver???

其它的細節
上邊已經提到了jni層的com_android_server_input_InputManagerService.cpp會調用java層InputManagerService的interceptKeyBeforeQueueing()、interceptKeyBeforeDispatching()、filterInputEvent()三個函數,下面我們再看看這3個函數的邏輯處理。其實在InputManagerService中還有很多其它jni函數,這些函數也都和native層InputManager的函數一一對應。
下面是這3個函數的代碼,很簡單的都傳給了回調。

    // Native callback.
    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
        return mWindowManagerCallbacks.interceptKeyBeforeQueueing(
                event, policyFlags, isScreenOn);
    }

    // Native callback.
    private long interceptKeyBeforeDispatching(InputWindowHandle focus,
            KeyEvent event, int policyFlags) {
        return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
    }

    // Native callback.
    final boolean filterInputEvent(InputEvent event, int policyFlags) {
        synchronized (mInputFilterLock) {
            if (mInputFilter != null) {
                try {
                    mInputFilter.filterInputEvent(event, policyFlags);
                } catch (RemoteException e) {
                    /* ignore */
                }
                return false;
            }
        }
        event.recycle();
        return true;
    }

前2個函數中的mWindowManagerCallbacks是在SystemServer將WindowManagerService的InputMonitor傳遞給了InputManagerService,而這個InputMonitor又將這些事件傳給了WindowManagerService中的WindowManagerPolicy,而這個WindowManagerPolicy的最終實現在/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。在PhoneWindowManager中,interceptKeyBeforeQueueing()函數主要處理power、call事件(這些事件優先級最高,直接關係到screen on/off和電話)相關一些邏輯,如果要實現一些自定義按鍵開關screen的操作,可以放在這裏處理;而interceptKeyBeforeDispatching()則主要處理了home、search、tab事件,這些事件都會直接啓動相應的一些界面,如果要實現一些自定義按鍵啓動應用的操作,可以放在這裏處理。

第3個函數中的InputFilter則是在AccessbilityManagerService中通過WindowManagerService設置到InputManagerService中的,是一個AccessbilityInputFilter實例。這些事件會由系統中的AccessbilityService做處理,之後再有InputFilterHost送回InputManagerService,再由InputManagerService通過jni接口nativeInjectInputEvent()送回給native層的InputDispatcher,並最終在InputDispatcher的enqueueInboundEventLocked()函數中進入事件隊列。接下來對這些filted的事件和其它事件就是一樣的處理流程了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章