Android Input(三)-InputReader获取事件

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

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

一、InputReader初始化

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

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

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

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
       const sp<InputReaderPolicyInterface>& policy,
       const sp<InputListenerInterface>& 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本身也是按事件类型来封装的。


InputDevice与InputMapper关系图
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<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

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

frameworks/native/services/inputflinger/InputListener.cpp

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& 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<KeyEntry*>(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线程。

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


事件获取流程大框架
事件获取调用时序图

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349