1. 程式人生 > >Android輸入子系統之應用程式註冊訊息監聽過程分析

Android輸入子系統之應用程式註冊訊息監聽過程分析

應用程式註冊訊息監聽過程分析

CPP層InputManagerService啟動後就需要監聽按鍵輸入了,當InputManagerService監聽到鍵盤輸入的事件後就需要分發鍵盤事件,但是分發給誰呢?這裡首先應該是分發給當前啟用的Window視窗,但是當前啟用的window視窗怎麼才能接受到訊息呢,window視窗需要註冊一個鍵盤訊息接收通道到InputManagerService中,那麼如何註冊的呢?我們知道一個Activity對應一個ViewRootImpl物件,在Activity啟動時會建立一個ViewRootImpl物件,並呼叫其setView函式把Activity的DecorView設定到ViewRootImpl中,而Activity正是在setView函式中註冊鍵盤訊息的接收通道的。
應用程式註冊訊息接收通道

Step 1. ViewRootImpl.setView

該函式定義在frameworks/base/core/java/android/view/ViewRootImpl.java中

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 
    //呼叫requestLayout來通知InputManagerService當前的視窗是啟用的視窗
    requestLayout();
    if ((mWindowAttributes.inputFeatures
            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0
) { //如果該視窗沒有指定INPUT_FEATURE_NO_INPUT_CHANNEL屬性,則建立訊息接收通道InputChannel mInputChannel = new InputChannel(); } try { //通過binder呼叫,呼叫server端的Session物件來跟WindowManagerService通訊,該函式最後會調 //用到WindowManagerService的addWindow函式,函式中會建立一對InputChannel(server/client), //這樣在函式呼叫結束後,mInputChannel就變成了client端的物件。在
//frameworks/base/core/java/android/view/IWindowSession.aidl的 //addToDisplay函式的宣告中,InputChannel指定的資料流的流向是out,因此 //WindowManagerService修改了mInputChannel,客戶端就能拿到這個物件的資料了。 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (Exception e) { ... } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); //初始化WindowInputEventReceiver,按鍵訊息會從native層傳到該物件的onInputEvent函式 //中,onInputEvent函式是按鍵在應用端java層分發的起始端。 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } } }

這個函式與註冊鍵盤訊息通道的相關主要有三個功能:
一是呼叫requestLayout函式來通知InputManagerService,這個Activity視窗是當前被啟用的視窗,同時將所有的視窗註冊到InputDispatcher中
二是呼叫mWindowSession的add成員函式來把鍵盤訊息接收通道的server端註冊端註冊到CPP層的InputManagerService中,client端註冊到本應用程式的訊息迴圈Looper中,這樣當InputManagerService監控到有鍵盤訊息的時候,就會找到當前被啟用的視窗,然後找到其在InputManagerService中對應的鍵盤訊息接收通道(InputChannel),通過這個通道在InputManagerService的server端來通知應用程式訊息迴圈的client端,這樣就把鍵盤訊息分發給當前啟用的Activity視窗了
三是應用程式這一側註冊訊息接收通道

Step 2. ViewRootImpl.requestLayout

該函式定義在frameworks/base/core/java/android/view/ViewRootImpl.java中

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }    
} 

這裡呼叫了scheduleTraversals函式來做進一步的操作,該函式呼叫mChoreographer來post一個Runnable到Looper中,之後會執行mTraversalRunnable中的run方法,即呼叫doTraversal函式

Step 3. ViewRootImpl.doTraversal

該函式定義在frameworks/base/core/java/android/view/ViewRootImpl.java中

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        performTraversals();
    }
}

該函式主要是執行performTraversals()函式,進而呼叫relayoutWindow函式,在該函式中又會呼叫mWindowSession的relayout進入到java層的WindowManagerService的relayoutWindow函式,該函式會呼叫mInputMonitor.updateInputWindowsLw(true /force/);mInputMonitor是InputMonitor物件。

Step 4. InputMonitor.updateInputWindowsLw

該函式定義在frameworks/base/core/java/android/view/ViewRootImpl.java中

public void updateInputWindowsLw(boolean force) {

    boolean addInputConsumerHandle = mService.mInputConsumer != null;

    // Add all windows on the default display.
    final int numDisplays = mService.mDisplayContents.size();
    for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
        WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
            final WindowState child = windows.get(winNdx);
            final InputChannel inputChannel = child.mInputChannel;
            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
            if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
                // Skip this window because it cannot possibly receive input.
                continue;
            }
            if (addInputConsumerHandle
                    && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
                addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
                addInputConsumerHandle = false;
            }

            final int flags = child.mAttrs.flags;
            final int privateFlags = child.mAttrs.privateFlags;
            final int type = child.mAttrs.type;

            final boolean hasFocus = (child == mInputFocus);
            final boolean isVisible = child.isVisibleLw();
            if ((privateFlags
                    & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
                        != 0) {
                disableWallpaperTouchEvents = true;
            }
            final boolean hasWallpaper = (child == mService.mWallpaperTarget)
                    && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
                    && !disableWallpaperTouchEvents;
            final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);

            // If there's a drag in progress and 'child' is a potential drop target,
            // make sure it's been told about the drag
            if (inDrag && isVisible && onDefaultDisplay) {
                mService.mDragState.sendDragStartedIfNeededLw(child);
            }

            addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
                    hasWallpaper);
        }
    }
    // Send windows to native code.
    mService.mInputManager.setInputWindows(mInputWindowHandles);
}

這個函式將當前系統中帶有InputChannel的Activity視窗都設定為InputManagerService的輸入視窗,但是後面我們會看到,只有當前啟用的窗口才會響應鍵盤訊息。

Step 5. InputManagerService.setInputWindows

該函式定義在frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public void setInputWindows(InputWindowHandle[] windowHandles) {
    nativeSetInputWindows(mPtr, windowHandles);
}

這個函式呼叫了本地方法nativeSetInputWindows來進一步執行操作,mPtr是native層NativeInputManager例項,在呼叫InputManagerService.nativeInit函式時會在native層構造NativeInputManager物件並將其儲存在mPtr中。nativeSetInputWindows會呼叫NativeInputManager的setInputWindows函式

Step 6. NativeInputManager.setInputWindows

這個函式定義在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
    Vector<sp<InputWindowHandle> > windowHandles;

    if (windowHandleObjArray) {
        jsize length = env->GetArrayLength(windowHandleObjArray);
        for (jsize i = 0; i < length; i++) {
            jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
            if (! windowHandleObj) {
                break; // found null element indicating end of used portion of the array
            }

            sp<InputWindowHandle> windowHandle =
                    android_server_InputWindowHandle_getHandle(env, windowHandleObj);
            if (windowHandle != NULL) {
                windowHandles.push(windowHandle);
            }
            env->DeleteLocalRef(windowHandleObj);
        }
    }

    mInputManager->getDispatcher()->setInputWindows(windowHandles);

    // Do this after the dispatcher has updated the window handle state.
    bool newPointerGesturesEnabled = true;
    size_t numWindows = windowHandles.size();
    for (size_t i = 0; i < numWindows; i++) {
        const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
                & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
            newPointerGesturesEnabled = false;
        }
    }
}

這個函式首先將Java層的InputWindowHandle轉換成C++層的NativeInputWindowHandle,然後放在windowHandles向量中,最後將這些輸入視窗設定到InputDispatcher中去。

Step 7. InputDispatcher.setInputWindows

這個函式定義在frameworks/native/services/inputflinger/InputDispatcher.cpp 檔案中:

void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
    { // acquire lock
        AutoMutex _l(mLock);

        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
        mWindowHandles = inputWindowHandles;

        sp<InputWindowHandle> newFocusedWindowHandle;
        bool foundHoveredWindow = false;
        for (size_t i = 0; i < mWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                mWindowHandles.removeAt(i--);
                continue;
            }
            if (windowHandle->getInfo()->hasFocus) {
                newFocusedWindowHandle = windowHandle;
            }
        }

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            mFocusedWindowHandle = newFocusedWindowHandle;
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}

這裡InputDispatcher的成員變數mFocusedWindowHandle 就代表當前啟用的視窗的。這個函式遍歷inputWindowHandles,獲取獲得焦點的視窗,並賦值給mFocusedWindowHandle 這樣,InputManagerService就把當前啟用的視窗儲存在InputDispatcher中了,後面就可以把鍵盤訊息分發給它來處理。

Step 8. 在前面的ViewRootImpl的setView中,下一步是server端註冊接收通道

if ((mWindowAttributes.inputFeatures
            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
        mInputChannel = new InputChannel();
    }
    try {
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
             getHostVisibility(), mDisplay.getDisplayId(),
             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
             mAttachInfo.mOutsets, mInputChannel);
    } catch (Exception e) {
        ...
    }

這裡會呼叫到WindowManagerService的addWindow介面

Step 9. WindowManagerService.addWindow

該函式定義在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
    WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
    Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
    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);
        }
}

這裡的outInputChannel即為前面建立的InputChannel,它不為NULL,因此,這裡會通過InputChannel.openInputChannelPair函式來建立一對輸入通道,其中一個位於WindowManagerService中,另外一個通過outInputChannel引數返回到應用程式中。WindowManagerService會為每個視窗建立一個WindowState物件,然後將該InputChannel對的service端儲存到WindowState中

Step 10. 下面看下InputChannel.openInputChannelPair的實現

函式定義在frameworks/base/core/java/android/view/InputChannel.java

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 + "'");
    }   
    return nativeOpenInputChannelPair(name);
} 

呼叫了nativeOpenInputChannelPair函式,在native建立一個InputChannel對

Step 11. InputChannel.nativeOpenInputChannelPair

函式定義在frameworks/base/core/jni/android_view_InputChannel.cpp

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8 name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    ...
    return channelPair;
}

nativeOpenInputChannelPair函式呼叫InputChannel的openInputChannelPair函式建立一對InputChannel,該物件是Native層的InputChannel,跟java層是一一對應的。

Step 12. InputChannel.openInputChannelPair

該函式定義在frameworks/native/libs/input/InputTransport.cpp中

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }   

    int bufferSize = SOCKET_BUFFER_SIZE;
    //設定server端和client端的接收緩衝區和傳送緩衝區
    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));

    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK; 
}

這裡呼叫了socketpair系統呼叫建立了一對已經連線的UNIX租socket,這裡可以把這一對socket當成pipe返回的檔案描述符一樣使用,pipe返回的管道是單向管道,即只能從一端寫入,一端讀出,但是socketpair是建立的管道是全雙工的,可讀可寫。

建立好了server端和client端通道後,在WindowManagerService.addWindow函式中,一方面它把剛才建立的Client端的輸入通道通過outInputChannel引數返回到應用程式中:

inputChannels[1].transferTo(outInputChannel);

另外還需要把server端的InputChannel註冊到InputManagerService中:

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

Step 13. InputManagerService.registerInputChannel

函式定義在frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public void registerInputChannel(InputChannel inputChannel,
        InputWindowHandle inputWindowHandle) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null.");
    }    

    nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}

通過呼叫nativeRegisterInputChannel來將InputChannel註冊到native層

Step 14. InputManagerService.nativeRegisterInputChannel

這個函式定義在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj); 

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
}

根據java層的InputWindowHandle獲得native層的InputWindowHandle物件,根據java層的InputChannel獲得native層的InputChannel物件,然後呼叫NativeInputManager的resgiterInputChannel,該函式又呼叫了InputDispatcher的registerInputChannel

Step 15. InputDispatcher.registerInputChannel

該函式定義在frameworks/native/services/inputflinger/InputDispatcher.cpp

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    { // acquire lock
        AutoMutex _l(mLock);

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }    

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

將InputWindowHandle, InputChanel封裝成Connection物件,然後fd作為key,Connection作為Value,儲存在mConnectionsByFd中,如果傳入的monitor是true,則需要將InputChannel放到mMonitoringChannels中,從上面的InputManagerService的registerInputChannel函式裡傳入的monitor是false,所以這裡不加入到mMonitoringChannels。同時把fd加入到mLooper的監聽中,並指定當該fd有內容可讀時,Looper就會呼叫handleReceiveCallback函式。至此server端的InputChannel註冊完成,InputDispatcher睡眠在監聽的fds上,當有按鍵事件發生時,InputDispatcher就會往這些fd寫入InputMessage物件,進而回調handleReceiveCallback函式。//TODO這裡當客戶端的程序讀取完事件後會往InputChannel的client端寫入????,而server端可以收到,這時InputDispatcher就會被喚醒,然後呼叫handleReceiveCallback來表明按鍵接收結束。

至此,server端的InputChannel就註冊完成了,再回到前面的WindowManagerService.addWindow上的第二步inputChannels[1].transferTo(outInputChannel);,這個是將建立的一對InputChannel的client端複製到傳入的引數InputChannel上,當addWindow返回時,就回到ViewRootImpl.setView函式中,執行應用程式這一側的鍵盤訊息接收通道。

if (mInputChannel != null) {
    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
        Looper.myLooper());
}

WindowInputEventReceiver繼承自InputEventReceiver類。

Step 16. InputEventReceiver.init

建構函式定義在frameworks/base/core/java/android/view/InputEventReceiver.java

public InputEventReceiver(InputChannel inputChannel, Looper looper) {

    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);

    mCloseGuard.open("dispose");
} 

呼叫了nativeInit執行native層的初始化

Step 17. InputEventReceiver.nativeInit

定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

函式建立了一個NativeInputEventReceiver物件,並呼叫其initialize函式

Step 18. NativeInputEventReceiver.initialize

該函式定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

呼叫setFdEvents函式

Step 19. NativeInputEventReceiver.setFdEvents

該函式定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

這裡呼叫傳入的MessageQueue獲取Looper物件,如果events是0,則表示要移除監聽fd,如果events不為0,表示要監聽fd,這個fd是前面WindowManagerService建立的一對InputChannel的client端,這樣當Server端寫入事件時,client端的looper就能被喚醒,並呼叫handleEvent函式(Looper::addFd函式可以指定LooperCallback物件,當fd可讀時,會呼叫LooperCallback的handleEvent,而NativeInputEventReceiver繼承自LooperCallback,所以這裡會呼叫NativeInputEventReceiver的handleEvent函式)

Step 20. NativeInputEventReceiver.handleEvent

該函式定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    } 
}

該函式呼叫consumeEvents函式來處理接收一個按鍵事件

Step 21. NativeInputEventReceiver.consumeEvents

該函式定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
                getInputChannelName(), consumeBatches ? "true" : "false", (long long)frameTime);
    }

    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (!skipCallbacks) {
            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;
            }

            if (inputEventObj) {
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                env->DeleteLocalRef(inputEventObj);
            }
        }

        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}

函式首先呼叫mInputConsumer.consume接收一個InputEvent物件,mInputConsumer在NativeInputEventReceiver建構函式中初始化

Step 22. InputConsumer.consume

該函式定義在frameworks/native/libs/input/InputTransport.cpp

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {

    *outSeq = 0;
    *outEvent = NULL;

    // 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.
            status_t result = mChannel->receiveMessage(&mMsg);
            if (result) {
                // Consume the next batched event unless batches are being held for later.
                if (consumeBatches || result != WOULD_BLOCK) {
                    result = consumeBatch(factory, frameTime, outSeq, outEvent);
                    if (*outEvent) {
                        break;
                    }   
                }   
                return result;
            }   
        }   

        switch (mMsg.header.type) {
        case InputMessage::TYPE_KEY: {
            KeyEvent* keyEvent = factory->createKeyEvent();
            if (!keyEvent) return NO_MEMORY;

            initializeKeyEvent(keyEvent, &mMsg);
            *outSeq = mMsg.body.key.seq;
            *outEvent = keyEvent;
            break;
        }   
   }
   return OK;
}

函式首先呼叫InputChannel的receiveMessage函式接收InputMessage物件,然後根據InputMessage物件呼叫initializeKeyEvent來構造KeyEvent物件。拿到可KeyEvent物件後,再對到consumeEvents中呼叫java層的InputEventReceiver.java的dispatchInputEvent函式

env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

Step 23. InputEventReceiver.dispatchInputEvent

該函式定義在frameworks/base/core/java/android/view/InputEventReceiver.java

// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
} 

進而呼叫onInputEvent函式。至此按鍵就開始了java層的分發。

參考