Android Framework input subsystem (07) APP establish contact

Series Interpretation & Description:

Android Framework Analysis of the input subsystem is divided into the following sections:

(01) and the core mechanism inotify epoll

(02) the core mechanism for two-way communication (socketpair + binder)

(03) an input system frame

(04) InputReader interpretation

(05) InputDispatcher interpretation

(06) Global Key A key to start the application case

(07) APP establish contact

(08)View基础(activity window decor view)

(09) InputStage interpretation

(10) Input common system tools (getevent & sendevent & input)

This module shared content: APP establish contact

This chapter summarizes the key points & Description:

The above is an iterative mapping, focusing ➕ APP can establish contact part, while the figure is an overall view, partial display of little, local shots, as follows:

After this section shown enlarged mind the above, this is mainly from three angles analyzed the process of establishing links between InputDispatcher described and APP.

FIG here first with a frame to represent the relationship between the InputReader, InputDispatcher, WindowManagerService, and application layer, as shown below:

The key principle here is simple to achieve, before it comes to the core mechanism is the use of socketpair, at the system level leaving a fd0, fd1 and then passed a binder in the application layer through inter-process communication mechanism, so, InputDispatcher system layer and application layer of We established a link between APP. System level server an event when the event reported by socketpair mechanism from the upper layer to the system.

Although the mechanism android system used is simple, but not easy to achieve in the code, briefly described herein architecture diagram of the flow:

  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之间建立联系的过程分析到此结束。

Guess you like

Origin blog.csdn.net/vviccc/article/details/93061885