Android Input(三)-InputReader获取事件

原创内容,转载请注明出处,多谢配合。

上篇简单交代了下输入子系统,那么这篇主要分析下InputReader获取事件过程。

一、InputReader初始化

从前面初始化的介绍中,我们知道InputReader是在InputManager构造方法中被初始化的。

frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

值得留意的是,InputDispatcher作为参数传入了InputReader的构造方法。
再看InputReader的构造方法:

InputReader::InputReader(const sp& eventHub,
       const sp& policy,
       const sp& listener) :
       mContext(this), mEventHub(eventHub), mPolicy(policy),
       mGlobalMetaState(0), mGeneration(1),
       mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
       mConfigurationChangesToRefresh(0) {
   mQueuedListener = new QueuedInputListener(listener);//这个listener对应的就是InputDispatcher
   { // acquire lock
       AutoMutex _l(mLock);
       refreshConfigurationLocked(0);
       updateGlobalMetaStateLocked();
   } // release lock
}

InputReader的构造函数中初始化了一个QueuedInputListener, 它接收InputListenerInterface作为它的参数,从InputReader调用可知,这个InputListenerInterface其实就是InputDispatcher, QueueInputListener只是作为InputDispatcher的Wrapper.这里先埋个伏笔。

二、InputReader运行

在InputManager执行start的时候InputReaderThread->run()

frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
   mReader->loopOnce();
   return true;
}

接着执行 mReader->loopOnce();

void InputReader::loopOnce() {
   …
   //获取事件
   size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
   { // acquire lock
       AutoMutex _l(mLock);
       mReaderIsAliveCondition.broadcast();
       if (count) {
           //处理事件
           processEventsLocked(mEventBuffer, count);
       }
   ...
   //传递事件
   mQueuedListener->flush();
}

这里主要做了三件事:获取事件、处理事件、传递事件,下面分别来看看:
2.1 获取事件

EventHub->getEvents上一篇已经分析了,主要就是获取kernel的event, 这里事件不仅包括input,还包括输入设备的add/remove等相关事件。加入的输入设备在没有事件的时候,会block在EventHub中的epoll处,当有事件产生后,epoll_wait就会返回,然后针对变化的事件进行处理。

2.2 处理事件

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
   for (const RawEvent* rawEvent = rawEvents; count;) {
       int32_t type = rawEvent->type;
       size_t batchSize = 1;
       if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
           int32_t deviceId = rawEvent->deviceId;
           while (batchSize < count) {
               if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                       || rawEvent[batchSize].deviceId != deviceId) {
                   break;
               }
               batchSize += 1;
           }
#if DEBUG_RAW_EVENTS
           ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
           //1、处理输入事件
           processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
       } else {
           switch (rawEvent->type) {
           case EventHubInterface::DEVICE_ADDED://2、添加输入设备
               addDeviceLocked(rawEvent->when, rawEvent->deviceId);
               break;
           case EventHubInterface::DEVICE_REMOVED://3、删除输入设备
               removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
               break;
           case EventHubInterface::FINISHED_DEVICE_SCAN://4、完成设备扫描
               handleConfigurationChangedLocked(rawEvent->when);
               break;
           default:
               ALOG_ASSERT(false); // can't happen
               break;
           }
       }
       count -= batchSize;
       rawEvent += batchSize;
   }
}

这里主要处理以上这4类事件,这里重点只关注下input event的收集流程:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
  ...
    InputDevice* device = mDevices.valueAt(deviceIndex);//获取到对应的device
   ...
    device->process(rawEvents, count);//device执行process操作
}

这里看到input的处理流程是由device执行process发起的。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
   size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
     ...
       if (mDropUntilNextSync) {
          ...
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {//对应的EV_KEY type走如下流程
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);//对应的InputMapper执行process操作
            }
        }
    }
}

这里执行的是mapper对应的process,而mapper实际上就是对应device中能匹配处理当前event的具体执行类。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
        if (isKeyboardOrGamepadKey(scanCode)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}

这里以案件事件EV_KEY为例,这里会执行processKey方法

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
   …
     //调用eventhub的mapKey
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
    ...
    }
    ...
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);//参数打包成args发送给Listener
}

最终将event组合成了一个notifykeyArgs数据结构,同时调用了listener的notifykey方法。

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

这里将NotifyKeyArgs放入了一个ArgsQueue中。

这部分简单来梳理一下:
InputReader收集不同设备输入信息,而不同设备对事件的处理又有分类,因为具体device的具体event交由对应的mapper来处理,而不匹配的则忽略。而mapper的处理主要是将event按NotifyArgs数据结构进行封装,并加入到一个ArgsQueue中。而NotifyArgs本身也是按事件类型来封装的。


Android Input(三)-InputReader获取事件_第1张图片
InputDevice与InputMapper关系图
Android Input(三)-InputReader获取事件_第2张图片
NotifyArgs的分类

2.3 传递事件

frameworks/native/services/inputflinger/InputListener.cpp

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

从ArgsQueue把事件取出来,分别调用它们自己的notify方法

以NotifyKeyArgs为例:

void NotifyKeyArgs::notify(const sp& listener) const {
    listener->notifyKey(this);
}

这个listener对应的是mInnerListener,那么要想知道Args传递到哪去了,就确定下mInnerListener是什么就好了。而在之前的初始化介绍部分已经埋过伏笔的,QueuedInputListener(listener);中传入的这个listener对应的就是InputDispatcher。

frameworks/native/services/inputflinger/InputListener.cpp

QueuedInputListener::QueuedInputListener(const sp& innerListener) :
       mInnerListener(innerListener) {
}

所以最终跟代码发现innerListener实际上就是InputDispatcher,notifyKey()就是InputDispacher的成员函数

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ... 
    int32_t keyCode = args->keyCode;
    ...
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime); 
  …
  mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
    int32_t repeatCount = 0;
    KeyEntry* newEntry = new KeyEntry(args->eventTime, args->deviceId, args->source, policyFlags,
           args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime);
    needWake = enqueueInboundEventLocked(newEntry);
    if (needWake) {
        mLooper->wake();//如果需要被唤醒,则唤醒dispatcher对应的Looper
    }
}

这里重点看两个方法:interceptKeyBeforeQueueing 与 enqueueInboundEventLocked

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ...
        if (keyEventObj) { //通过jni方式回调java层
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }
    ...
    } 
}

这部分其实就是PhoneWindowManager先做一次事件拦截处理操作,也就解释了为什么有些点击事件是不会传给应用的,而是在PhoneWindowManager那里就已经被拦截了。具体逻辑不铺开分析。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
   bool needWake = mInboundQueue.isEmpty();
   mInboundQueue.enqueueAtTail(entry);
   traceInboundQueueLengthLocked();
   switch (entry->type) {
   case EventEntry::TYPE_KEY: {
       KeyEntry* keyEntry = static_cast(entry);
       ...
       break;
   }
   ...
   }
   return needWake;
}

这里将keyArgs转换成KeyEntery这种数据封装,并添加到InputDispatcher的mInboundQueue里,最后唤醒InputDispacher线程。

最后简单总结一下整个InputReader获取事件流程:
在InputManager执行start的时候InputReaderThread->run(),接着会执行 mReader->loopOnce();
内部逻辑主要分三块:
获取事件:
getEvents时,打开"/dev/input/"目录下的input设备,并将其注册到epoll的监控队列中。这样一旦对应设备上有可读的input事件,则epool_wait()就会返回,并带回deviceid,找到具体的device。整个事件的获取中,除了input事件,设备的打开关闭等信息,也要包装成event,上报给InputReader。

处理事件:
processEventsForDeviceLocked 调用对应的InputDevice处理Input事件,而InputDevice又会去匹配上对应的InputMapper来处理对应事件。(在InputDevice中,存储着许多InputMapper,每种InputMapper对应一类Device,例如:Touch、Keyboard、Vibrator等等……)而调用InputDevice的process函数,就是将Input事件传递给每一个InputMapper,匹配的InputMapper就会对Input事件进行处理,不匹配的则会忽略。而对应的InputMapper最终会按对应的NotifyArgs数据结构对事件进行封装,并加入到ArgsQueue中。

传递事件:
flush会将ArgsQueue中的每一个Arg取出来交由innerListener对应的InputDispacher执行对应类型的notify方法,将keyArgs转换成KeyEntery这种数据封装,并添加到InputDispatcher的mInboundQueue里,最后唤醒InputDispacher线程。

最后放上整体流程图以及时序图


Android Input(三)-InputReader获取事件_第3张图片
事件获取流程大框架
Android Input(三)-InputReader获取事件_第4张图片
事件获取调用时序图

到这里,整个的input事件的获取就已经完成了。

下一篇文章:
Android Input(四) -InputDispatcher分发事件

参考:
https://www.jianshu.com/p/2bff4ecd86c9 借用两张图

你可能感兴趣的:(Android Input(三)-InputReader获取事件)