Android IMS原理解析之InputChannel

       接着上篇文章Android IMS原理解析之InputDispatcher的分析,本文主要分析Input事件如何发送到对应窗口,事件发送及处理反馈主要是通过InputChannel来进行的,结合Connection、InputPublisher、InputConsumer等,接下来一起分析一下:

Input事件发送到窗口

       上面讲到,InputDispatcher在事件派发时最终会通过Connection调用到InputPublisher内部的publishMotionEvent()方法,先看一下Connection这个类,该类定义在InputDispatcher.h中:

1.Connection
InputDispatcher::Connection::Connection(const sp& inputChannel,
        const sp& inputWindowHandle, bool monitor) :
        status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
        monitor(monitor),
        inputPublisher(inputChannel), inputPublisherBlocked(false) {
}

class Connection : public RefBase {
    protected:
        virtual ~Connection();

    public:
        enum Status {
            // Everything is peachy.
            STATUS_NORMAL,
            // An unrecoverable communication error has occurred.
            STATUS_BROKEN,
            // The input channel has been unregistered.
            STATUS_ZOMBIE
        };

        Status status;
        sp inputChannel; // never null
        sp inputWindowHandle; // may be null
        bool monitor;
        InputPublisher inputPublisher;
        InputState inputState;

        bool inputPublisherBlocked;

        // Queue of events that need to be published to the connection.
        Queue outboundQueue;

        Queue waitQueue;

        explicit Connection(const sp& inputChannel,
                const sp& inputWindowHandle, bool monitor);

        inline const char* getInputChannelName() const { return inputChannel->getName().string(); }

        const char* getWindowName() const;
        const char* getStatusLabel() const;

        DispatchEntry* findWaitQueueEntry(uint32_t seq);
}

       可以看到,该类内部定义了InputChannel、InputWindowHandle、InputPublisher等变量,是对以上对象的封装,Connection是事件发送的入口,主要的发送逻辑是由封装的类对象执行的;

2.InputPublisher

       该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp,看一下publishMotionEvent()的实现:

//创建InputPublisher实例时传入InputChannel
InputPublisher::InputPublisher(const sp& channel) :
        mChannel(channel) {
}

status_t InputPublisher::publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source,int32_t displayId, 
        int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags,int32_t metaState,
        int32_t buttonState,float xOffset,float yOffset,float xPrecision,float yPrecision,nsecs_t downTime,
        nsecs_t eventTime,uint32_t pointerCount,const PointerProperties* pointerProperties,
        const PointerCoords* pointerCoords) {
    .....
    ......
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
    msg.body.motion.seq = seq;
    msg.body.motion.deviceId = deviceId;
    msg.body.motion.source = source;
    msg.body.motion.displayId = displayId;
    msg.body.motion.action = action;
    msg.body.motion.actionButton = actionButton;
    msg.body.motion.flags = flags;
    msg.body.motion.edgeFlags = edgeFlags;
    msg.body.motion.metaState = metaState;
    msg.body.motion.buttonState = buttonState;
    msg.body.motion.xOffset = xOffset;
    msg.body.motion.yOffset = yOffset;
    msg.body.motion.xPrecision = xPrecision;
    msg.body.motion.yPrecision = yPrecision;
    msg.body.motion.downTime = downTime;
    msg.body.motion.eventTime = eventTime;
    msg.body.motion.pointerCount = pointerCount;
    for (uint32_t i = 0; i < pointerCount; i++) {
        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
    }
    return mChannel->sendMessage(&msg);
}

       在publishMotionEvent()内部,最终是将MotionEvent封装成InputMessage,然后调用mChannel->sendMessage(&msg),mChannel是InputChannel实例;

3.InputChannel

       该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp

status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
        int error = errno;
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (size_t(nWrite) != msgLength) {
        return DEAD_OBJECT;
    }

    return OK;
}

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);

    if (nRead < 0) {
        int error = errno;
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (nRead == 0) { // check for EOF
        return DEAD_OBJECT;
    }

    if (!msg->isValid(nRead)) {
        return BAD_VALUE;
    }
    return OK;
}

       可以看到,sendMessage()和receiveMessage()内部分别对应send()和recv(),从方法名可以看到,是通过socket进行传输的,接下来通过以下几个方面来对InputChannel进行分析:
       3.1.InputChannel是什么
       InputChannel本质是一对SocketPair(非网络套接字)。SocketPair用来实现在本机内进行进程间的通信。一对SocketPair通过socketpair()函数创建,其使用者可以因此而得到两个相互连接的文件描述符。这两个描述符可以通过套接字接口send()和recv()进行写入和读取,并且向其中一个文件描述符写入的数据,可以从另一个描述符中读取。同pipe()所创建的管道不同,SocketPair的两个文件描述符是双通的,因此非常适合用来进行进程间的交互式通信;
       3.2.创建InputChannel
       我们知道当一个Window需要显示时,最终都会调用到WMS的addWindow()方法:

//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int   viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {
    ......
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
    //仅当窗口的inputFeatures未指定INPUT_FEATURE_NO_INPUT_CHANNEL选项时才会为此窗口创建InputChannel对
    final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    if  (openInputChannels) {
        win.openInputChannel(outInputChannel);
    }
    ......

    return res;
}

       在内部创建WindowState代表具体的窗口,然后执行openInputChannel()方法:

//WindowState.java
void openInputChannel(InputChannel outInputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
    }
    String name = getName();
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    mInputChannel = inputChannels[0];
    mClientChannel = inputChannels[1];
    mInputWindowHandle.inputChannel = inputChannels[0];
    if (outInputChannel != null) {
        mClientChannel.transferTo(outInputChannel);
        mClientChannel.dispose();
        mClientChannel = null;
    }
    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

       在openInputChannel()内部执行了三项工作:
       1.调用InputChannel的openInputChannelPair()来返回InputChannel[]数组,数组大小为2;
       2.将mInputChannel及mInputWindowHandle.inputChannel赋值为inputChannels[0],将mClientChannel赋值为inputChannels[1],然后执行transferTo转换为outInputChannel;
       3.执行registerInputChannel();
       先看openInputChannelPair()方法:

//InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {
    .......
    return nativeOpenInputChannelPair(name);
}

       该方法会调用nativeOpenInputChannelPair(),即native方法,路径位于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 serverChannel;
    sp clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

       可以看到,会调用 InputChannel::openInputChannelPair来创建serverChannel, clientChannel,然后创建数组channelPair,将serverChannel, clientChannel处理后存入数组,返回给java层,该逻辑实现代码路径为:frameworks/native/libs/input/InputTransport.cpp,一起看一下方法实现:

status_t InputChannel::openInputChannelPair(const String8& name,
        sp& outServerChannel, sp& outClientChannel) {
    int sockets[2];
    //通过socketpair()函数创建一对无名的相互连接的socket,并保存在sockets数组中
    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));

    //创建server端的InputChannel对象
    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    //创建client端的InputChannel对象
    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

       用一张流程图总结一下创建InputChannel过程:

openInputChannel.png

       3.3.连接到InputDispatcher
       前面讲到,在WindowState的openInputChannel()方法中最后会执行mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle)来进行连接,一起看一下调用流程:

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

       在registerInputChannel()方法内部会调用nativeRegisterInputChannel(),会调用到native层,代码路径为: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(ptr);

    sp inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

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

       可以看到,在nativeRegisterInputChannel()方法内部会调用到NativeInputManager的registerInputChannel()方法:

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp& inputChannel,
        const sp& inputWindowHandle, bool monitor) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}

       通过InputManager最终调用到InputDispatcher的registerInputChannel()方法:

status_t InputDispatcher::registerInputChannel(const sp& inputChannel,
        const sp& inputWindowHandle, bool monitor) 

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

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

       可以看到,在InputDispatcher的registerInputChannel()内部会执行四项工作:
       1.根据传入的inputChannel、inputWindowHandle等参数创建Connection实例;
       2.获取inputChannel的Fd,然后以键值对形式将fd及connection存入mConnectionsByFd中,后续在进行窗口事件派发时,会从该mConnectionsByFd里面查找到对应的Connection;
       3.执行mLooper->addFd,后续fd有数据收到时,会回调handleReceiveCallback方法;
       4.调用mLooper->wake();
       在前面InputDispatcher的事件派发中代码讲到,会根据目标窗口找到对应的InputWindowHandle,然后根据以下方式来找到对应的Connection,即从mConnectionsByFd中根据fd来匹配到对应的Connection,先注册后发送,前后对应起来了;

ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
     sp connection = mConnectionsByFd.valueAt(connectionIndex);
     prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}

       registerInputChannel()为InputChannel创建了一个Connection,并监听了InputChannel的可读事件,使得InputDispatcher拥有了将事件发送给InputChannel并接受反馈的能力。
       用一张流程图总结一下连接InputDispatcher过程:

连接到Dispatcher.png

       3.4.连接到窗口
       前面分析到,在openInputChannelPair()时会创建一对InputChannel,一个是在addWindow()时通过registerInputChannel()时传入到InputDispatcher,另外一个是用来接收InputDispatcher传来的事件,是在窗口端,接下来一起看一下:
       窗口添加的入口是在ViewRootImpl,相关执行流程可以参考Android View 显示原理分析,看一下setView()方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ......
    ......
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                   getHostVisibility(), mDisplay.getDisplayId(),
                   mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                   mAttachInfo.mOutsets, mInputChannel);
    ......
    ......
    if (mInputChannel != null) {
        ........
        mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
    }
    ......
}

       当窗口端通过addWindow()函数获取InputChannel后,便会使用它创建一个InputEventReceiver对象。InputEventReceiver对象可以接收来自InputChannel的输入事件,并触发其onInputEvent()回调,看一下WindowInputEventReceiver的实现:

final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }

    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }

    .......
}

       WindowInputEventReceiver继承InputEventReceiver,在创建对象时调用了父类的构造方法,接下来看一下InputEventReceiver:

private static native long nativeInit(WeakReference receiver,
            InputChannel inputChannel, MessageQueue messageQueue);

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

    mCloseGuard.open("dispose");
}

       可以看到,在构造方法内部会执行nativeInit(),将inputChannel及messageQueue传入,返回NativeInputEventReceiver对象引用mReceivePtr,后续在事件处理完后调用finishInputEvent()发送完成消息时会用到,nativeInit()是native方法,会调用到native层,对应的实现代码路径为:frameworks/base/core/jni/android_view_InputEventReceiver.cpp

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

    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ......
    sp 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(receiver.get());
}

       创建了NativeInputEventReceiver对象携带inputChannel,然后执行initialize()方法,先看一下NativeInputEventReceiver构造方法:

class NativeInputEventReceiver : public LooperCallback {
    .......
}

NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp& inputChannel,
        const sp& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
}

       NativeInputEventReceiver的构造函数很简单,它保存了java层InputEventReceiver对象的引用,并创建了一个InputConsumer类型的对象对InputChannel进行封装。
       InputConsumer与InputPublisher一样,它也封装了InputChannel,负责对其进行写入和读取操作,同时也负责InputMessage的封装与解析。不过它们的功能正好相反,InputConsumer接收的是输入事件,发送的则是反馈。
       接下来看一下initialize()方法:

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

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

       在initialize()内部执行了setFdEvents(),然后在setFdEvents()内部获取到InputChannel的fd,然后通过Looper->addFd()对InputChannel的可读性事件进行监听,此时就已经连接到窗口了;
       用一张流程图总结一下连接窗口过程:


连接到窗口.png

       当有InputMessage可读时,NativeInputEventReceiver的handleEvent()函数会被Looper调用,此时便可以通过InputConsumer从InputChannel中读取事件,然后回调到java层的onInputEvent()函数,在java层完成事件的处理后,便可通过InputConsumer发送处理完毕的反馈给InputDispatcher。
       接下来看一下handleEvent()是如何将事件传给java层的onInputEvent()方法:

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

       在handleEvent()内部会调用到consumeEvents():

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ......
    for (;;) {
        ......

        InputEvent* inputEvent;
        int32_t displayId;
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent, &displayId,
                &motionEventType, &touchMoveNum, &flag);

        .......

        case AINPUT_EVENT_TYPE_MOTION: {
            MotionEvent* motionEvent = static_cast(inputEvent);
            if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
                *outConsumedBatch = true;
            }
            inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
            break;
        }

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

       在consumeEvents()内部主要做了三项工作:
       1.调用InputConsumer的consume()从InputChannel中读取一条InputMessage,解析为InputEvent后通过inputEvent参数传出;InputConsumer的实现也是在InputTransport.cpp中,跟InputPublisher对应;
       2.根据事件的类型分别创建KeyEvent与MotionEvent类型的java对象;
       3.通过JNI回调java层的InputEventReceiver的dispatchInputEvent()函数;

4.InputConsumer

       前面第一项工作中涉及到InputConsumer,该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp,该类是和InputPublisher对应的,一个来发送,一个来消费处理,处理完毕后发送finish signal;

       接着上面进行分析,再看一下Java层的处理:

private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event, displayId);
}

       dispatchInputEvent函数首先在字典中保存来自InputDispatcher的事件序列号,以满足发送反馈之需。之后便调用onInputEvent()函数,交由子类进行输入事件的实际处理工作。onInputEvent()函数可由使用者重写,从而实现各种各样的工作,Android控件根ViewRootImpl在收到事件后会将其派发给特定的控件。

5.简单总结

       1.事件发送主要是通过InputChannel来完成;
       2.在wms 执行addView()时,调用openInputChannel来从native层获取inputchannels数组,一个通过ims registerInputChannel来连接InputDispatcher,另外一个通过InputEventReceiver来连接窗口;
       3.InputDispatcher经过Connection最终通过InputPublisher将事件发送到目标窗口;
       4.NativeInputEventListener监听到事件到来时通过InputConsumer处理InputMessage后回调Java层接口;

       到这里事件就已经发送到对应的窗口了,接下来看一下事件的处理Android IMS原理解析之processEvent

你可能感兴趣的:(Android IMS原理解析之InputChannel)