目录

1.EventHub->getEvents()

2.processEventsLocked()

3.mQueuedListener->flush()


前面讲到,在start()后会启动InputThread线程不断的从EventHub中抽取原始输入事件并进行加工处理,代码如下:

frameworks/native/services/inputflinger/reader/InputReader.cpp

status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}

threadLoop中执行的是loopOnce函数
frameworks/native/services/inputflinger/reader/InputReader.cpp

void InputReader::loopOnce() {...size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //1{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();if (count) {processEventsLocked(mEventBuffer, count); //2}...mQueuedListener->flush();//3
}

InputReader一次线程循环,即执行一次loopOnce(),主要执行了三项工作:

注释一:从EventHub中获取未处理的事件列表,读取的结果存储在参数mEventBuffer中,返回值表示事件的个数;当EventHub中无事件可抽取时,此函数的调用将会阻塞直到事件到来或者超时;事件信息主要有两种,一种是设备节点的增删事件:如添加/移除设备的事件,一种是原始输入事件:如触摸屏幕或者按物理按键触发的事件。

注释二:通过processEventsLocked()对事件进行处理(两种事件都处理),对于原始输入事件,进行转译、封装与加工后将结果暂存到mQueuedListener中;

注释三:发布事件,所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher;

接下来对这三件事逐一分析:

1.EventHub->getEvents()

调用EventHub的getEvents()方法来获取事件列表,EventHub是如何工作的呢?

EventHub的直译是事件集线器,它将所有的输入事件通过一个接口getEvents()把从多个输入设备节点中读取的事件交给InputReader,它是输入系统最底层的一个组件,使用了INotify与Epoll两套机制,通过构造方法就可以看到:

/frameworks/native/services/inputflinger/reader/EventHub.cpp

283  EventHub::EventHub(void)
284        : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
285          mNextDeviceId(1),
286          mControllerNumbers(),
287          mOpeningDevices(nullptr),
288          mClosingDevices(nullptr),
289          mNeedToSendFinishedDeviceScan(false),
290          mNeedToReopenDevices(false),
291          mNeedToScanDevices(true),
292          mPendingEventCount(0),
293          mPendingEventIndex(0),
294          mPendingINotify(false) {
295      ensureProcessCanBlockSuspend();296  //1.使用epoll_create()函数创建一个epoll对象。EPOLL_SIZE_HINT指定最大监听个数为8,这个 // epoll对象将用来监听设备节点是否有数据可读(有无事件)
297      mEpollFd = epoll_create1(EPOLL_CLOEXEC);
298      LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
299      //2.创建一个inotify对象。这个inotify对象将被用来监听设备节点的增删事件
300      mINotifyFd = inotify_init();//将存储设备节点的路径/dev/input作为监听对象添加到inotify对象中。当此文件夹下的设备节点 //发生创建与删除事件时,//都可以通过mINotifyFd读取事件的详细信息
301      mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
302      LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
303                          strerror(errno));
304      if (isV4lScanningEnabled()) {
305          mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
306          LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
307                              VIDEO_DEVICE_PATH, strerror(errno));
308      } else {
309          mVideoWd = -1;
310          ALOGI("Video device scanning disabled");
311      }//3.将mINotifyFd作为epoll的一个监控对象。当inotify事件到来时,epoll_wait()将立即返回,//EventHub便可以从mINotifyFd中读取设备节点的增删信息,并进行相应处理
313      struct epoll_event eventItem = {};
314      eventItem.events = EPOLLIN | EPOLLWAKEUP;
315      eventItem.data.fd = mINotifyFd;//将对mINotifyFd的监听注册到epoll对象中
316      int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
317      LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
318      //创建一个名为wakeFds的匿名管道
319      int wakeFds[2];
320      result = pipe(wakeFds);
321      LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
322
323      mWakeReadPipeFd = wakeFds[0];
324      mWakeWritePipeFd = wakeFds[1];
325
326      result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
327      LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
328                          errno);
329
330      result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
331      LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
332                          errno);
333
334      eventItem.data.fd = mWakeReadPipeFd;//将管道读取端的描述符的可读事件注册到epoll对象中。因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在//epoll_wait()的调用里,然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。此时只需向wakeFds管道的写入端写入任意数据,//此时读取端有数据可读,使得epoll_wait()得以返回,从而达到唤醒InputReader线程的目的
335      result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
336      LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
337                          errno);
338  }

在上面的构造方法内,对Epoll和INotify进行处理,后续有事件到来时就可以获取了,反正知道会监视目录文件  /dev/input就可以了。接下来看一下getEvents()的逻辑处理:

/frameworks/native/services/inputflinger/reader/EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {.......for (;;) {.......//处理未被InputReader取走的输入事件与设备事件。epoll_wait()所取出的epoll_event存储在mPendingEventItems中,//mPendingEventCount指定mPendingEventItems数组所存储的事件个数。而mPendingEventIndex指定尚未处理的epoll_event的索引while (mPendingEventIndex < mPendingEventCount) {const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];//在这里分析每一个epoll_event 如果表示设备节点可读,则读取原始事件并放置到buffer中。//如果表示mINotifyEd可读,则设置mPendingINotify为true,当InputReader将现有的输入事件都取出后读取mINotifyEd中的事件,并加装与卸载相应的设备。//另外,如果此epoll_event表示wakeFds的读取端有数据可读,则设置awake标志为true,//此时无论此次getEvents()调用是否取到事件,都不会调用epoll_wait()进行事件等待。if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {if (eventItem.events & EPOLLIN) {mPendingINotify = true;}continue;}if (eventItem.data.u32 == EPOLL_ID_WAKE) {if (eventItem.events & EPOLLIN) {char buffer[16];ssize_t nRead;do {nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));}}}.....//如果此次getEvents()调用成功获取了一些事件,或者要求唤醒InputReader,则退出循环并结束getEvents()的调用,//使InputReader可以立刻处理事件if (event != buffer || awoken) {break;}//如果此次getEvents()调用没能获取事件,说明mPendingEventItems中没有事件可用。//于是执行epoll_wait()函数等待新的事件到来,将结果存储到mPendingEventItems里,并重置mPendingEventIndex为0mPendingEventIndex = 0;int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);if (pollResult == 0) {// Timed out.mPendingEventCount = 0;break;}if (pollResult < 0) {// An error occurred.mPendingEventCount = 0;if (errno != EINTR) {ALOGW("poll failed (errno=%d)\n", errno);usleep(100000);}} else {//从epoll_wait()中得到新的事件后,重新循环,对新事件进行处理mPendingEventCount = size_t(pollResult);}}//返回本次getEvents()调用所读取的事件数量return event - buffer;
}

getEvents()函数的本质就是读取并处理Epoll事件与INotify事件:包括设备插拔及各种触摸、按钮事件等,可以看做是一个不同设备的集线器,主要面向的是/dev/input目录下的设备节点,比如说/dev/in,通过EventHubput/event0上的事件就是输入事件的getEvents()就可以监听并获取该事件,getEvents()将它们封装为RawEvent结构体,并放入mEventBuffer中供InputReader进行处理。如果没有输入事件将调用epoll_wait()函数阻塞等待。

2.processEventsLocked()

当通过EventHub获取到事件即count>0时,对事件进行加工处理,此时调用到processEventsLocked()方法:

frameworks/native/services/inputflinger/reader/InputReader.cpp

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) { //1int32_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: %zu Count: %zu", batchSize, count);}processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {switch (rawEvent->type) { //2case EventHubInterface::DEVICE_ADDED://增加设备addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED://移除设备removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN://扫描设备结束handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}

processEventsLocked()函数中会遍历mEventBuffer中存储的输入事件,处理rawEvent分成两类:注释1处理原始输入事件,注释2处理设备事件。

1.先看处理设备事件:

对于添加、删除设备的event事件处理比较简单,只需要增加和删除mDevices列表中Device对象就完成了,而对于扫描设备结束的Event还需调用handleConfigurationChangedLocked()函数来处理设备的配置文件。mDevices的定义,是一个map集合,保存着eventHubId和InputDevice对象

2.对于原始输入事件:

调用processEventsForDeviceLocked函数进行处理代码如下:

frameworks/native/services/inputflinger/reader/InputReader.cpp

269  void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
270                                                 size_t count) {
271      auto deviceIt = mDevices.find(eventHubId);
272      if (deviceIt == mDevices.end()) {
273          ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
274          return;
275      }
276
277      std::shared_ptr<InputDevice>& device = deviceIt->second;
278      if (device->isIgnored()) {
279          // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
280          return;
281      }
282
283      device->process(rawEvents, count);
284  }

注释1处根据eventHubId查询map集合中是否有该key,如果没有则返回,有则在注释2处将value值取出,是InputDevice类型的。在注释3处调用它的process函数执行处理

frameworks/native/services/inputflinger/reader/InputDevice.cpp

void InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved.  For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {if (DEBUG_RAW_EVENTS) {ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,rawEvent->when);}if (mDropUntilNextSync) { //1if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;if (DEBUG_RAW_EVENTS) {ALOGD("Recovered from input event buffer overrun.");}} else {if (DEBUG_RAW_EVENTS) {ALOGD("Dropped input event while waiting for next input sync.");}}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());mDropUntilNextSync = true;reset(rawEvent->when);} else {for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {mapper.process(rawEvent); //2});}--count;}
}

注释1处的mDropUntilNextSync的值默认为false,如果设备的输入缓冲区溢出,该值会被设置为true,如果恢复,会重新置为false。原始输入事件类型有很多,如键盘输入事件、触摸输入事件等,这些类别都会由单独的InputMapper的子类来进行处理。

注释2处遍历所有的Mapper类型,选择合适的Mapper处理事件,for_each_mapper_in_subdevice是一个内联函数,用于遍历所有的Mapper,InputReader将输入事件交由合适的InputMapper处理,至于是哪个InputMapper,InputReader并不关心。这里以键盘输入事件为例,执行的是KeyboardInputMapper的process函数:

frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

void KeyboardInputMapper::process(const RawEvent* rawEvent) {switch (rawEvent->type) {case EV_KEY: { //1按键事件的处理int32_t scanCode = rawEvent->code;int32_t usageCode = mCurrentHidUsage;mCurrentHidUsage = 0;if (isKeyboardOrGamepadKey(scanCode)) {processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,usageCode); //2}break;}case EV_MSC: { //3其他事件的处理if (rawEvent->code == MSC_SCAN) {mCurrentHidUsage = rawEvent->value;}break;}case EV_SYN: { //4同步事件的处理if (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}}
}

注释3处为其他事件的处理,注释4处为同步事件的处理。主要还是关注注释1处,按键类型事件,调用注释2处processKey函数进行处理。

frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,int32_t usageCode) {...NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,getDisplayId(), policyFlags,down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);getListener().notifyKey(&args);
}

这里会对键盘输入事件进行加工,并将加工后的数据封装到NotifyKeyArgs类型的对象中。然后调用getListener()函数得到InputListenerInterface类型的对象。InputDispatcherListener继承了InputListenerInterface,而InputDispatcher继承了InputDispatcherListener,所以这里调用的实际是InputDispatcher的notifyKey函数。此处先不分析notifyxx()逻辑,稍后一起分析:

3.mQueuedListener->flush()

在porceeEventsLocked()后,接下来会执行mQueuedListener->flush()来结束loopOnce()这个方法,mQueuedListener是QueuedInputListener实例,看看里面的逻辑:

/frameworks/native/services/inputflinger/InputListener.cpp


326  void QueuedInputListener::notifyConfigurationChanged(
327          const NotifyConfigurationChangedArgs* args) {
328      traceEvent(__func__, args->id);
329      mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
330  }
331
332  void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
333      traceEvent(__func__, args->id);
334      mArgsQueue.push_back(new NotifyKeyArgs(*args));
335  }
336
337  void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
338      traceEvent(__func__, args->id);
339      mArgsQueue.push_back(new NotifyMotionArgs(*args));
340  }
341
342  void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
343      traceEvent(__func__, args->id);
344      mArgsQueue.push_back(new NotifySwitchArgs(*args));
345  }
346
347  void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
348      traceEvent(__func__, args->id);
349      mArgsQueue.push_back(new NotifySensorArgs(*args));
350  }
351
352  void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
353      traceEvent(__func__, args->id);
354      mArgsQueue.push_back(new NotifyVibratorStateArgs(*args));
355  }
356
357  void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
358      traceEvent(__func__, args->id);
359      mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
360  }
361
362  void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
363      traceEvent(__func__, args->id);
364      mArgsQueue.push_back(new NotifyPointerCaptureChangedArgs(*args));
365  }
366
367  void QueuedInputListener::flush() {
368      size_t count = mArgsQueue.size();
369      for (size_t i = 0; i < count; i++) {
370          NotifyArgs* args = mArgsQueue[i];
371          args->notify(mInnerListener);
372          delete args;
373      }
374      mArgsQueue.clear();
375  }
376
377  }

可以看到,在第二项工作中调用notifyKey()后,执行了队列的push,只是将该事件args压入队列中;最后通过flush()来遍历队列,执行对应args的notify()方法.........(省略一万字)。

QueuedInputListener避免了在原始事件的加工过程中向InputDispatcher进行事件提交,而是将事件信息缓存起来,在InputReader::loopOnce()函数的末尾,也就是InputReader处理完抽取自EventHub的所有原始输入事件之后,QueuedInputListener::flush()函数的调用将缓存的事件信息取出,并提交给InputDispatcher,事件派发就由此开始了。

安卓IMS 原理解析(二、IMS之InputReader事件获取)相关推荐

  1. OkHttp原理解析(二)

    前言 上一篇我们学习了OKHttp的请求执行流程,知道了最终请求流程都会交给getResponseWithInterceptorChain方法来执行,接下来我们就详细分析执行getResponseWi ...

  2. hessian原理解析二(服务端分析)

    hessian 服务端源码分析 我们在回头看看 web.xml 中 servlet 配置 <servlet> <servlet-name>HelloHessian</se ...

  3. iOS中下载大型文件的原理解析二

    在iOS中下载大型文件,需要使用NSURLConnection 的代理方法: (void)touchesBegan:(NSSet)touches withEvent:(UIEvent *)event ...

  4. Config原理解析,项目搭建以及如何获取不同环境下的配置文件信息(一)

    这里写目录标题 原理 搭建config项目 新增github仓库 spring-cloud-config-server spring-cloud-config-client 如何获取的不同环境下的配置 ...

  5. 【Pikachu】漏洞练习平台做题记录+原理解析(2.2)XSS姿势和技巧

    前言 Pikachu是一个带有漏洞的Web应用系统,在这里包含了常见的web安全漏洞. 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习,那么Pikachu可能正合你意. pikach ...

  6. ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解析

    ThreadLocal系列之InheritableThreadLocal的使用及原理解析(源码基于java8) 上一篇:ThreadLocal系列(一)-ThreadLocal的使用及原理解析 下一篇 ...

  7. Spring Boot:(二)启动原理解析

    Spring Boot:(二)启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏. ...

  8. 二维码(QR code)原理解析

    二维码(QR code)原理解析 基于 <GB/T 18284-2000 快速响应矩阵码> 文章目录 二维码(QR code)原理解析 1 模式 1.1 ECI 模式 1.2 数字模式 1 ...

  9. Handler机制原理解析(二)prepare,loop,post

    Handler机制原理解析(二)prepare,loop,post 上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一).这一篇,介绍下Handler周边的知识点 ...

最新文章

  1. 6000星人气深度学习资源!架构模型技巧全都有,图灵奖得主LeCun推荐
  2. ps 毛发 边缘_不会抠图怎么办?PS画笔绘制毛发技巧,抠图流程解析
  3. FlashCC学习札记
  4. Linux用管道移动文件夹,常用的Linux上的文件管理类命令讲解及演示
  5. 理解position:relative 与 position:absolute
  6. java并发编程实战电子书,王者笔记!
  7. 基于朴素贝叶斯算法的新闻分类
  8. Facebook原型网站FaceMash域名拍得3万美元
  9. 《Java程序设计》实验指导——项目3 类与对象
  10. H5满屏彩色泡泡小特效(适合表白哦~做完发给让你每天想念的人吧~)
  11. 计算机1500字论文,当我打开电脑_1500字
  12. php可以发短信的代码,php实现短信发送代码
  13. WinCE快捷方式浅析
  14. 220609_Efficient Uncertainty-aware Decision-making for Automated Driving Using Guided Branching
  15. 机器学习——期末复习
  16. 公用Foot和友情链接模块代码模板
  17. 说说python程序的执行过程_《师说》的“说”
  18. 985材料专业研究生转行
  19. 南京信息工程大学计算机科学与技术怎么样,作为全国重点的大学,南京信息工程大学是个怎么样的学校?...
  20. 大雁蛋人工孵化的方法_青大湖里大雁弃25枚蛋逃跑 3只孵化破壳(图)

热门文章

  1. rtsp流转码为rtmp/http-flv流的视频监控/直播系统中,视频流路数越来越多时,服务器端对CPU、内存、网络带宽的压力越来越大的解决方法
  2. VUE+WebPack游戏设计:欲望都市城市图层的设计
  3. 在线制作简易业务流程图
  4. 企业非法集资风险预测
  5. 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
  6. js实现图片放大预览(jq+css)
  7. 语音-小度自定义技能
  8. 我的世界神级种子Java_盘点我的世界中单机生存必备的神级种子
  9. 自学mysql还是sql好_都说自学SQL数据库难,是真的吗?
  10. 【mq】从零开始实现 mq-09-消费者拉取消息 pull message