天天看點

Android Framework 輸入子系統(07)APP建立聯系

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

本章關鍵點總結 & 說明:

Android Framework 輸入子系統(07)APP建立聯系

以上是疊代導圖,主要關注➕ APP 建立聯系部分即可,同時上圖是總圖,局部顯示的有點小,局部截圖,如下所示:

Android Framework 輸入子系統(07)APP建立聯系

本章節的思維導圖放大後如上所示,這裡主要從三個角度對InputDispatcher與APP之間建立聯系的過程 進行分析說明。

這裡首先用一張架構圖來表示InputReader,InputDispatcher,WindowManagerService,與應用層之間的關系,如下所示:

Android Framework 輸入子系統(07)APP建立聯系

這裡的關鍵實作原理很簡單,核心就是使用之前談到的socketpair機制,在系統層留下一個fd0,再通過binder程序間通信機制在應用層傳遞一個fd1,這樣,系統層的InputDispatcher和應用層的APP之間建立起了一個聯系。系統層服務端有事件的時候通過socketpair機制把事件從系統層上報到上層。

android系統雖然使用的機制很簡單,但在代碼實作上并不簡單,這裡簡單說明下架構圖的流程:

  1. 在InputDispatcher内部,socketpair被封裝成了inputchannel,inputchannel又被封裝成了Connection進而和應用層之間進行聯系
  2. 在WindowManagerService内部,WindowState通過建立socketpair來得到fd0和fd1,将fd0封裝成inputchannel注冊到InputDispatcher中,将fd1通過binder通信機制傳遞給應用端,
  3. 應用端将fd1封裝成InputChannel,通過WindowInputEventReceiver最後将fd1放入到應用層這邊的looper中進行監聽

這就是整個事件傳輸的宏觀流程,接下來我們從3個方面對 輸入事件與APP之間建立聯系,做一個分析:

  1. WindowManager注冊視窗焦點
  2. server端消息注冊通道
  3. client端注冊消息接收通道

在android系統中一個Activity對應一個ViewRootImpl對象,在Activity啟動時會執行handleResumeActivity,這裡會建立一個ViewRootImpl對象,并調用其setView函數把Activity的DecorView設定到ViewRootImpl中,而Activity正是在setView函數中注冊鍵盤消息的接收通道的。對于應用程式一定會調用到ViewRootImpl,是以我們從這裡開始分析,ViewRootImpl的setView關鍵代碼如下:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 
    //關鍵點1:調用requestLayout來通知InputManagerService目前的視窗是激活的視窗
    requestLayout();
    if ((mWindowAttributes.inputFeatures
            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
        mInputChannel = new InputChannel();
    }
    try {
        //關鍵點2,bindr通信,後面會建立一對InputChannel(server/client)
        //函數調用結束後,mInputChannel就變成了client端的對象。
        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);
            //關鍵點3 建立并初始化WindowInputEventReceiver,按鍵消息會從native層傳到該對象的onInputEvent函數
            //說明:onInputEvent函數是按鍵在應用端java層分發的起始端
            mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                    Looper.myLooper());
        }
    }
}
           

這裡關注➕上面3個關鍵點:

  1. requestLayout是通知IMS這個Activity視窗是目前被激活的視窗,同時将所有的視窗注冊到InputDispatcher中。
  2. mWindowSession.addToDisplay是把鍵盤消息接收通道的server端注冊端注冊到CPP層的IMS中,client端注冊到本應用程式的消息循環Looper中,當IMS監控到有鍵盤消息的時候,就會找到目前被激活的視窗,進而找到其在IMS中對應的鍵盤消息接收通道(InputChannel),通過這個通道在IMS的server端來通知應用程式消息循環的client端,這樣就把鍵盤消息分發給目前激活的Activity視窗了 
  3. 應用程式這一側注冊消息接收通道

1 WindowManager注冊視窗焦點流程

這裡繼續分析requestLayout,代碼實作如下:

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

這裡調用 scheduleTraversals函數來做進一步的操作,代碼如下:

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
        }
    }
           

該函數調用mChoreographer來post一個Runnable到Looper中,之後會執行mTraversalRunnable中的run方法,即調用doTraversal函數,代碼實作如下:

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

這裡主要是執行performTraversals()函數,代碼實作如下:

private void performTraversals() {
    //...
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    //...
}
           

這裡關鍵調用relayoutWindow函數,在該函數中又會調用mWindowSession的relayout,實作如下:

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
//...
int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingConfiguration, mSurface);
//...
}
           

進而進入到WMS的relayoutWindow函數,這裡關注mInputMonitor.updateInputWindowsLw(true ),代碼如下:

public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
            Surface outSurface) {
    //...
    mInputMonitor.updateInputWindowsLw(true /*force*/);
    //...
}
           

這裡的mInputMonitor是InputMonitor對象。,這裡關注mInputMonitor.updateInputWindowsLw的實作,代碼如下:

public void updateInputWindowsLw(boolean force) {
        //...
        // 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;
                }

                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 (inDrag && isVisible && onDefaultDisplay) {
                    mService.mDragState.sendDragStartedIfNeededLw(child);
                }

                if (universeBackground != null && !addedUniverse
                        && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
                    final WindowState u = universeBackground.mWin;
                    if (u.mInputChannel != null && u.mInputWindowHandle != null) {
                        addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
                                u.mAttrs.type, true, u == mInputFocus, false);
                    }
                    addedUniverse = true;
                }

                if (child.mWinAnimator != universeBackground) {
                    addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible,
                            hasFocus, hasWallpaper);
                }
            }
        }

        // Send windows to native code.
        mService.mInputManager.setInputWindows(mInputWindowHandles);

        // Clear the list in preparation for the next round.
        clearInputWindowHandlesLw();
    }
           

這裡将帶有InputChannel的Activity視窗都設定為IMS的輸入視窗,最後執行mService.mInputManager.setInputWindows,代碼如下:

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

該navtive函數定義在 frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp中,實作如下:

static void nativeSetInputWindows(JNIEnv* env, jclass clazz,
        jlong ptr, jobjectArray windowHandleObjArray) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    im->setInputWindows(env, windowHandleObjArray);
}
           

這裡繼續分析im->setInputWindows,代碼實作如下:

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);
    //...
}
           

這個函數首先将Java層的InputWindowHandle轉換成C++層的NativeInputWindowHandle,然後放在windowHandles向量中,最後将這些輸入視窗設定到InputDispatcher中去。最後關注InputDispatcher的setInputWindows,代碼實作如下:

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 (windowHandle == mLastHoverWindowHandle) {
                foundHoveredWindow = true;
            }
        }

        if (!foundHoveredWindow) {
            mLastHoverWindowHandle = NULL;
        }

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            if (mFocusedWindowHandle != NULL) {
                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
                if (focusedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                            "focus left window");
                    synthesizeCancelationEventsForInputChannelLocked(
                            focusedInputChannel, options);
                }
            }
            if (newFocusedWindowHandle != NULL) {
            }
            mFocusedWindowHandle = newFocusedWindowHandle;
        }
        //...
    } // release lock

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

這裡InputDispatcher的成員變量mFocusedWindowHandle 就代表目前激活的視窗。這個函數周遊inputWindowHandles,擷取獲得焦點的視窗,并指派給mFocusedWindowHandle 。這樣,IMS就把目前激活的視窗儲存在InputDispatcher中,後面就可以把鍵盤消息分發給它來處理。

2 server端消息注冊通道流程

這裡從關注mWindowSession.addToDisplay開始,最終會調用到WMS的addWindow接口,代碼實作如下:

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
        //...
        synchronized(mWindowMap) {
            //...
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            //...
            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                //關鍵點1
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);
                //關鍵點2
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }
            //...
        }
        //...
        return res;
    }
           

這裡會通過InputChannel.openInputChannelPair函數來建立一對輸入通道,一個位于WMS中,另外一個通過outInputChannel參數傳回到APP中。WMS會為每個視窗建立一個WindowState對象,然後将該InputChannel對的service端儲存到WindowState中。

2.1 分析openInputChannelPair,代碼實作如下:

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,代碼實作如下:

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;
}
           

這裡InputChannel的openInputChannelPair函數建立一對InputChannel,該對象是Native層的InputChannel,跟java層對應。

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;
    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;
}
           

建立好了server和client端後,在WMS的addWindow函數中,把剛建立的Client端的輸入通道通過outInputChannel參數傳回到應用程式中,如下:

nputChannels[1].transferTo(outInputChannel);
           

另外還把server端的InputChannel注冊到IMS中,也就是接下來要分析的部分。

2.2 分析registerInputChannel,代碼實作如下:

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

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

繼續分析nativeRegisterInputChannel,代碼實作如下:

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);
	//...

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}
           

這裡根據java層的InputWindowHandle獲得native層的InputWindowHandle對象,根據java層InputChannel獲得native層InputChannel對象,最後調用NativeInputManager的resgiterInputChannel,進而調用InputDispatcher的registerInputChannel,代碼實作如下:

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

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }

        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中,同時把fd加入到mLooper的監聽中,并指定當該fd有内容可讀時,Looper就會調用handleReceiveCallback函數。InputDispatcher睡在監聽的fds上,當有按鍵事件發生時,InputDispatcher就會往這些fd寫入InputMessage對象,進而回調handleReceiveCallback函數。

3 client端注冊消息接收通道流程

關注➕ 前面的WindowManagerService.addWindow上的第二步:inputChannels[1].transferTo(outInputChannel);這是将建立的一對InputChannel的client端複制到傳入的參數InputChannel上,當addWindow傳回時,就回到ViewRootImpl.setView函數中,執行應用程式這一側的鍵盤消息接收通道。接下來分析代碼:

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

WindowInputEventReceiver是繼承InputEventReceiver的,看InputEventReceiver的構造器,代碼如下:

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
	//...
    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);

    mCloseGuard.open("dispose");
}
           

這裡繼續分析nativeInit,代碼如下:

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函數,代碼如下:

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

接續分析setFdEvents,代碼如下:

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);
        }
    }
}
           

這裡 如果events是0,則表示要移除監聽fd,如果events不為0,表示要監聽fd,這個fd是前面WMS建立的一對InputChannel的client端,當Server端寫入事件時,client端的looper就能被喚醒,并調用handleEvent函數(當fd可讀時,會調用LooperCallback的handleEvent,而NativeInputEventReceiver繼承自LooperCallback,是以這裡會調用NativeInputEventReceiver的handleEvent函數),這裡分析NativeInputEventReceiver.handleEvent,代碼實作如下:

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        return 0; // remove the callback
    }

    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函數,它是用來處理接收一個按鍵事件,代碼如下:

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        //關鍵點1
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (status) {
            if (status == WOULD_BLOCK) {
                if (!skipCallbacks && !mBatchedInputEventPending
                        && mInputConsumer.hasPendingBatch()) {
                    // There is a pending batch.  Come back later.
                    if (!receiverObj.get()) {
                        receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                        if (!receiverObj.get()) {
                            return DEAD_OBJECT;
                        }
                    }
                    mBatchedInputEventPending = true;
                    //關鍵點2
                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
                    if (env->ExceptionCheck()) {
                        mBatchedInputEventPending = false; // try again later
                    }
                }
                return OK;
            }
            return status;
        }
		//...
        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}
           

@1 InputConsumer.consume的實作如下所示:

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
    *outSeq = 0;
    *outEvent = NULL;
	
    // Loop until an event can be returned or no additional events are received.
    while (!*outEvent) {
        if (mMsgDeferred) {
            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;
}


           

這裡忽略motion的處理,先通過InputChannel的receiveMessage函數接收InputMessage,根據InputMessage對象調用initializeKeyEvent來構造KeyEvent對象。這裡便拿到了KeyEvent對象。

@2 調用dispatchInputEvent的關鍵代碼,如下所示

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

拿到keyEvent對象後,再consumeEvents中調用java層的InputEventReceiver.java的dispatchInputEvent函數來派發事件,代碼實作如下:

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

這裡調用到onInputEvent函數。接下來輸入事件就到了java層的分發。緻此,Dispatcher線程與WMS,以及APP之間建立聯系的過程分析到此結束。