1. 程式人生 > >按鍵事件在native和jni中的流程

按鍵事件在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的事件和其它事件就是一樣的處理流程了。