Step 21. NativeInputQueue.handleReceiveCallback
这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- sp<Connection> connection;
- InputEvent* inputEvent;
- jobject inputHandlerObjLocal;
- jlong finishedToken;
- { // acquire lock
- AutoMutex _l(q->mLock);
- ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- ......
- status_t status = connection->inputConsumer.receiveDispatchSignal();
- if (status) {
- ......
- return 0; // remove the callback
- }
- ......
- status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
- ......
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- } // release lock
- ......
- int32_t inputEventType = inputEvent->getType();
- jobject inputEventObj;
- jmethodID dispatchMethodId;
- switch (inputEventType) {
- case AINPUT_EVENT_TYPE_KEY:
- ......
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
- break;
- }
- ......
- }
- ......
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
- ......
- return 1;
- }
这个函数首先是通过参数data获得当初注册InputChannel的NativeInputQueue对象,具体可以参考前面介绍的应用程序注册键盘消息接收通道过程的Step 21。接下来再通过参数receiveFd获得保存在这个NativeInputQueue对象中的mConnectionsByReceiveFd成员变量中的Connection对象。有了这个Connection对象后,就可以获得它内部的InputConsumer对象,这个InputConsumer对象是和上面的Step 18中介绍的InputPublisher对象相应的。
在InputChannel内部中,分别有一个InputPublisher对象和一个InputConsumer对象,对于Server端的InputChannel来说,它使用的是InputPublisher对象,通过它进行键盘消息的分发,而对于Client端的InputChannel来说,它使用的是InputConsumer对象,通过它进行键盘消息的读取。
获得了这个InputConsumer对象后首先是调用它的receiveDispatchSignal来确认是否是接收到了键盘消息的通知,如果是的话,再调用它的consume函数来把键盘事件读取出来,最后,调用Java层的回调对象InputQueue的DispatchKeyEvent来处理这个键盘事件。下面,我们就依次来分析这些过程。
Step 22. InputConsumer.receiveDispatchSignal
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputConsumer::receiveDispatchSignal() {
- ......
- char signal;
- status_t result = mChannel->receiveSignal(& signal);
- if (result) {
- return result;
- }
- if (signal != INPUT_SIGNAL_DISPATCH) {
- ......
- return UNKNOWN_ERROR;
- }
- return OK;
- }
这个函数很简单,它通过它内部对象mChannel来从前向管道的读端读入一个字符,看看是否是前面的Step 20中写入的INPUT_SIGNAL_DISPATCH字符。
InputChannel类的receiveSignal函数也是定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputChannel::receiveSignal(char* outSignal) {
- ssize_t nRead;
- do {
- nRead = ::read(mReceivePipeFd, outSignal, 1);
- } while (nRead == -1 && errno == EINTR);
- if (nRead == 1) {
- ......
- return OK;
- }
- ......
- return -errno;
- }
Step 23. InputConsumer.consume
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
- ......
- *outEvent = NULL;
- int ashmemFd = mChannel->getAshmemFd();
- int result = ashmem_pin_region(ashmemFd, 0, 0);
- ......
- if (mSharedMessage->consumed) {
- ......
- return INVALID_OPERATION;
- }
- // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal
- // to the publisher that the message has been consumed (or is in the process of being
- // consumed). Eventually the publisher will reinitialize the semaphore for the next message.
- result = sem_wait(& mSharedMessage->semaphore);
- ......
- mSharedMessage->consumed = true;
- switch (mSharedMessage->type) {
- case AINPUT_EVENT_TYPE_KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (! keyEvent) return NO_MEMORY;
- populateKeyEvent(keyEvent);
- *outEvent = keyEvent;
- break;
- }
- ......
- }
- return OK;
- }
这个函数很简单,只要对照前面的Step 18(InputPublisher.publishKeyEvent)来逻辑来看就可以了,后者是往匿名共享内存中写入键盘事件,前者是从这个匿名共享内存中把这个键盘事件的内容读取出来。
回到Step 21中的handleReceiveCallback函数中,从InputConsumer中获得了键盘事件的内容(保存在本地变量inputEvent中)后,就开始要通知Java层的应用程序了。在前面分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),会把传进来的对象inputHandlerObj保存在Connection对象中:
- connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
这个inputHandlerObj对象的类型为Java层的InputHandler对象,因此,这里首先把它取回来:
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
取回来之后,我们要把作为参数来调用InputQueue类的dispatchKeyEvent静态成员函数来通知应用程序,有键盘事件发生了,因此,先找到InputQueue类的静态成员函数dispatchKeyEvent的ID:
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
在回调用这个InputQueue类的dispatchKeyEvent静态成员函数之前,还要把前面获得的inputEvent对象转换成Java层的KeyEvent对象:
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
万事具备了,就可以通知Java层的InputQueue来处理这个键盘事件了:
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));