Browsed by
分类:Android

input之我见05——EVENTHUB兼谈EPOLL机制

input之我见05——EVENTHUB兼谈EPOLL机制

1,引子

Input事件从Kernel层传到用户层,传输节点通过/dev/input/下面的节点:
  1. p253:/dev/input # ls -al
  2. crw-rw---- 1 root input 13, 64 2015-01-01 00:00 event0
  3. crw-rw---- 1 root input 13, 65 2015-01-01 00:00 event1
  4. crw-rw---- 1 root input 13, 66 2015-01-01 00:00 event2
  5. crw-rw---- 1 root input 13, 63 2015-01-01 00:00 mice
从Kernel层的INPUT子系统层面,各个INPUT设备完成初始化之后,都是通过input_event()这个函数“上报”输入事件。深究这个“上报”,我们发现是input_event()往/dev/input/下面的某个event的节点里,按一定结构写数据包(type,code,value),而用户层是有专门的线程监听这个节点,一旦有数据更新,就立马读取,传传递给其它线程进一步加工处理。
从第一讲中的草图可以看到这些信息。
这里面讲的监听线程就是EventHub,而实现这种读取传递的机制就是EPOLL机制。这两个东西就是今天需要讲的。

2,EventHub的前世今生
2.1,构造
Android7.0 Input事件处理这篇文章可以知道,EventHub是在InputReader构造的时候,传进入去的参数。那么EventHub的构造是在哪里?再继续往上跟踪可以发现,EventHub的构造是在com_android_server_input_InputManagerService.cpp的NativeInputManager方法中
  1. NativeInputManager::NativeInputManager(jobject contextObj,
  2. jobject serviceObj, const sp<Looper>& looper) :
  3. mLooper(looper), mInteractive(true) {
  4. ...
  5. sp<EventHub> eventHub = new EventHub();
  6. mInputManager = new InputManager(eventHub, this, this);
  7. }
2.2,入口
同样在上一篇文章中,EventHub从InputMangaer的构造中传入,又作为参数传给了InputReader
  1. InputManager::InputManager(
  2. const sp<EventHubInterface>& eventHub,
  3. const sp<InputReaderPolicyInterface>& readerPolicy,
  4. const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  5. mDispatcher = new InputDispatcher(dispatcherPolicy);
  6. mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  7. initialize();
  8. }
这里的EventHubInterface是EventHub的父类,所以这类型也没问题。InputReader在自己的线程执行函数中,通过这个eventHub来获取各个Input设备的数据。
  1. ...
  2. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
  3. ...
参数timeoutMillis,设置一个超时时间;
参数mEventBuffer,是一个RawEvent数组;
  1. // The event queue.
  2. static const int EVENT_BUFFER_SIZE = 256;
  3. RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
参数EVENT_BUFFER_SIZE,即buffer大小。
3,EventHub的构造
先看一下相关类图
 再看代码实现
  1. EventHub::EventHub(void) {
  2. ...
  3. //获得epoll fd和inotify fd
  4. mEpollFd = epoll_create(EPOLL_SIZE_HINT);
  5. mINotifyFd = inotify_init();
  6. //通过inotifyFd监听/dev/input这个目录
  7. int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
  8. //实例化epoll_event结构体
  9. struct epoll_event eventItem;
  10. memset(&eventItem, 0, sizeof(eventItem));
  11. eventItem.events = EPOLLIN;
  12. eventItem.data.u32 = EPOLL_ID_INOTIFY;
  13. //设置notify FD
  14. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
  15. //初始化PipeFD,并设置PipeFd

  16. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
这里面主要是初始化了一些EPOLL机制,这也就进入我这篇文章的第二个主机,EPOLL机制
4,EPOLL机制
4.1,Linux IO操作模型
说到EPOLL机制,不得不提到Linux的IO操作模型,大概有如下几种模型
  • 同步阻塞IO
  • 同步非阻塞IO
  • 多路复用IO
  • 异步IO
%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
IO模型 定义 过程图示 数据等待阶段 数据复制阶段 优点 缺点
同步阻塞IO  
%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
BLOCK BLOCK 能够及时返回数据,无延迟;  
同步非阻塞IO 同步非阻塞就是
“每隔一会儿瞄一眼进度条” 的轮询(polling)方式

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
NON-BLOCK BLOCK 能够在等待任务完成的时间里干其他活了 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作
多路复用IO UNIX/Linux
下的 select、poll、epoll

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
NON-BLOCK BLOCK 监听多个Iofd  
异步IO 用户进程发起aio_read操作之后,立刻就可以开始去做其它的事,当这一切都完成之后,kernel会给用户进程发送一个signal或执行一个基于线程的回调函数来完成这次
IO 处理过程

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
       
EventHub使用的EPOLL机制是一种多路利用IO模型,习惯把这种模型归类为同步IO操作,其实无所以,来具体看看这种机制

4.2,EPOLL模型
看看相关的数据结构
3种类型
  1. /* Valid opcodes to issue to sys_epoll_ctl() */
  2. #define EPOLL_CTL_ADD 1
  3. #define EPOLL_CTL_DEL 2
  4. #define EPOLL_CTL_MOD 3
epoll_event数据结构
  1. struct epoll_event {
  2. __u32 events;
  3. __u64 data;
  4. } EPOLL_PACKED;
通知的两种类型
  1. // Ids used for epoll notifications not associated with devices.
  2. static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
  3. static const uint32_t EPOLL_ID_WAKE = 0x80000002;
重要的方法
  1. intepoll_create(int size);
  2. 生成一个Epoll专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个Epoll fd上能关注的最大socket fd数,大小自定,只要内存足够。
  3. intepoll_ctl(int epfd, intop, int fd, structepoll_event *event);
  4. 控制某个Epoll文件描述符上的事件:注册、修改、删除。其中参数epfdepoll_create()创建Epoll专用的文件描述符。相对于select模型中的FD_SETFD_CLR宏。
  5. intepoll_wait(int epfd,structepoll_event * events,int maxevents,int timeout);
  6. 等待I/O事件的发生;参数说明:
  7. epfd:由epoll_create() 生成的Epoll专用的文件描述符;
  8. epoll_event:用于回传代处理事件的数组;
  9. maxevents:每次能处理的事件数;
  10. timeout:等待I/O事件发生的超时值;
  11. 返回发生事件数。
回到前面,在EventHub构造函数中,设置Epoll的最大数,获得了epoll_fd,之后,每打开一个Input设置,都会把这个设备的fd注册进epoll里面去
openDevLocked();
  1. // Register with epoll.
  2. struct epoll_event eventItem;
  3. memset(&eventItem, 0, sizeof(eventItem));
  4. eventItem.events = EPOLLIN;

  5. eventItem.data.u32 = deviceId;
  6. if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
  7. ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
  8. delete device;
  9. return -1;
  10. }
epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。
getEvents();
  1. //在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);之后,读到的总event_poll保存在mPendingEventCount,mPendingEventIndex初始化为0,所以这里面的逻辑是一个个取出来
  2. while (mPendingEventIndex < mPendingEventCount) {
  3. const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
  4. //EPOLL_ID_INOTIFY类型事件
  5. if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
  6. if (eventItem.events & EPOLLIN) {
  7. mPendingINotify = true;
  8. } else {
  9. ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
  10. }
  11. continue;
  12. }
  13. //EPOLL_ID_WAKE事件
  14. if (eventItem.data.u32 == EPOLL_ID_WAKE) {
  15. if (eventItem.events & EPOLLIN) {
  16. ALOGV("awoken after wake()");
  17. awoken = true;
  18. char buffer[16];
  19. ssize_t nRead;
  20. do {
  21. //进行数据读取
  22. nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
  23. } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
  24. } else {
  25. ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
  26. eventItem.events);
  27. }
  28. continue;
  29. }
  30. ...
  31. Device* device = mDevices.valueAt(deviceIndex);
  32. if (eventItem.events & EPOLLIN) {
  33. int32_t readSize = read(device->fd, readBuffer,
  34. sizeof(struct input_event) * capacity);
  35. ...
  36. } else {
  37. int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
  38. //开始解析Buffuer
  39. size_t count = size_t(readSize) / sizeof(struct input_event);
  40. for (size_t i = 0; i < count; i++) {
  41. struct input_event& iev = readBuffer[i];
  42. ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
  43. device->path.string(),
  44. (int) iev.time.tv_sec, (int) iev.time.tv_usec,
  45. iev.type, iev.code, iev.value);
  46. // Some input devices may have a better concept of the time
  47. // when an input event was actually generated than the kernel
  48. // which simply timestamps all events on entry to evdev.
  49. // This is a custom Android extension of the input protocol
  50. // mainly intended for use with uinput based device drivers.
  51. if (iev.type == EV_MSC) {
  52. if (iev.code == MSC_ANDROID_TIME_SEC) {
  53. device->timestampOverrideSec = iev.value;
  54. continue;
  55. } else if (iev.code == MSC_ANDROID_TIME_USEC) {
  56. device->timestampOverrideUsec = iev.value;
  57. continue;
  58. }
  59. }
  60. if (device->timestampOverrideSec || device->timestampOverrideUsec) {
  61. iev.time.tv_sec = device->timestampOverrideSec;
  62. iev.time.tv_usec = device->timestampOverrideUsec;
  63. if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
  64. device->timestampOverrideSec = 0;
  65. device->timestampOverrideUsec = 0;
  66. }
  67. ALOGV("applied override time %d.%06d",
  68. int(iev.time.tv_sec), int(iev.time.tv_usec));
  69. }
  70. // Use the time specified in the event instead of the current time
  71. // so that downstream code can get more accurate estimates of
  72. // event dispatch latency from the time the event is enqueued onto
  73. // the evdev client buffer.
  74. //
  75. // The event's timestamp fortuitously uses the same monotonic clock
  76. // time base as the rest of Android. The kernel event device driver
  77. // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
  78. // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
  79. // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
  80. // system call that also queries ktime_get_ts().
  81. event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
  82. + nsecs_t(iev.time.tv_usec) * 1000LL;
  83. ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
  84. // Bug 7291243: Add a guard in case the kernel generates timestamps
  85. // that appear to be far into the future because they were generated
  86. // using the wrong clock source.
  87. //
  88. // This can happen because when the input device is initially opened
  89. // it has a default clock source of CLOCK_REALTIME. Any input events
  90. // enqueued right after the device is opened will have timestamps
  91. // generated using CLOCK_REALTIME. We later set the clock source
  92. // to CLOCK_MONOTONIC but it is already too late.
  93. //
  94. // Invalid input event timestamps can result in ANRs, crashes and
  95. // and other issues that are hard to track down. We must not let them
  96. // propagate through the system.
  97. //
  98. // Log a warning so that we notice the problem and recover gracefully.
  99. ...
  100. event->deviceId = deviceId;
  101. event->type = iev.type;
  102. event->code = iev.code;
  103. event->value = iev.value;
  104. event += 1;
  105. capacity -= 1;
  106. }
  107. if (capacity == 0) {
  108. // The result buffer is full. Reset the pending event index
  109. // so we will try to read the device again on the next iteration.
  110. mPendingEventIndex -= 1;
  111. break;
  112. }
  113. }
  114. }
  115. ...
这样就把数据读出来了

参考:
input之我见04——InputMangerService(Android7.0)

input之我见04——InputMangerService(Android7.0)

先抛出一个问题:
interceptKeyBeforQueueing 与 interceptKeyBeforDispatch的执行顺序。
很早以前…..
InputManager.cpp实例化的时候,会创建两个线程
  1. InputManager::InputManager(
  2. const sp<EventHubInterface>& eventHub,
  3. const sp<InputReaderPolicyInterface>& readerPolicy,
  4. const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  5. mDispatcher = new InputDispatcher(dispatcherPolicy);
  6. mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  7. initialize();
  8. }
  9. ...
  10. void InputManager::initialize() {
  11. mReaderThread = new InputReaderThread(mReader);
  12. mDispatcherThread = new InputDispatcherThread(mDispatcher);
  13. }
紧接着就start()
  1. status_t InputManager::start() {
  2. status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
  3. ...
  4. result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
  5. }
从此这两个线程就各自生活,貌似分道扬镳,其实却有着联系。
=>

InputReader

1,先看InputReader的构造函数,构造是在InputManager完成,
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
mDispatcher是InputDispacher的实例,而InputDispacher是继承了InputListenerInterface接口(继承爷爷接口),在此作为参数传递给InputReader,构造成mQueueListener变量,这就是InputReader与InputDispacher通信的桥梁。
=》
  1. InputReader::InputReader(const sp<EventHubInterface>& eventHub,
  2. const sp<InputReaderPolicyInterface>& policy,
  3. const sp<InputListenerInterface>& listener) :
  4. mContext(this), mEventHub(eventHub), mPolicy(policy),
  5. mGlobalMetaState(0), mGeneration(1),
  6. mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
  7. mConfigurationChangesToRefresh(0) {
  8. mQueuedListener = new QueuedInputListener(listener);
  9. { // acquire lock
  10. AutoMutex _l(mLock);
  11. refreshConfigurationLocked(0);
  12. updateGlobalMetaStateLocked();
  13. } // release lock
  14. }
来看InputReaderThread()的loopOnce(),是在InputReaderThread进行调用。
  1. bool InputReaderThread::threadLoop() {
  2. mReader->loopOnce();
  3. return true;
  4. }
InputReader.cpp里面
  1. void InputReader::loopOnce() {
  2. ...
  3. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
  4. ...
  5. if (count) {
  6. processEventsLocked(mEventBuffer, count);
  7. }
  8. ...
  9. mQueuedListener->flush();
  10. }
去掉一些不重要的代码,loopOnce做了3件事
  • 通过EventHub->getEvents读取底层Input设备,是否有数据产生;
  • 如果有数据,就通过processEventLocked处理元数据,转化为input数据;
  • 处理完成之后,通过mQueueListener->flush()通知InputDispatcher去接收数据。
重点来看ProcessEventsLocked()
=>
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
=>
device->process(rawEvents, count);
=>
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
通过不同的mapper来调用相应的process函数,我们这里关注KeyBoard
=>
void KeyboardInputMapper::process(const RawEvent* rawEvent)
=>
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
=>
把rawEvent封装成NotifyKeyArgs的指针,
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
然后再传给某个Listener的nofifyKey,这个Listener就是InputListenerInterface,notifyKey调用就是InputDispacher的notifyKey。
getListener()->notifyKey(&args);
=>
进入InputDisaptcher.cpp
=>
  1. void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
  2. ...
  3. mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
  4. ...
  5. }
找到,interceptKeyBeforeQueueing终于又回到问题的出发点了。接下来看另外一处世界

InputDispacher

同理,先看InputDispacher的构造函数
  1. InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
  2. mPolicy(policy),
  3. mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
  4. mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
  5. mNextUnblockedEvent(NULL),
  6. mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
  7. mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
  8. mLooper = new Looper(false);
  9. mKeyRepeatState.lastKeyEntry = NULL;
  10. policy->getDispatcherConfiguration(&mConfig);
  11. }
没啥要特别讲的,那么进入线程的loop函数
  1. bool InputDispatcherThread::threadLoop() {
  2. mDispatcher->dispatchOnce();
  3. return true;
  4. }
=>
  1. if (!haveCommandsLocked()) {
  2. dispatchOnceInnerLocked(&nextWakeupTime);
  3. }
如果有数据就进入dispachOnceInnerLocked,进一步处理。
=>
同理,我们只关注Key的处理事件
  1. void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
  2. ...
  3. case EventEntry::TYPE_KEY: {
  4. KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
  5. if (isAppSwitchDue) {
  6. if (isAppSwitchKeyEventLocked(typedEntry)) {
  7. resetPendingAppSwitchLocked(true);
  8. isAppSwitchDue = false;
  9. } else if (dropReason == DROP_REASON_NOT_DROPPED) {
  10. dropReason = DROP_REASON_APP_SWITCH;
  11. }
  12. }
  13. if (dropReason == DROP_REASON_NOT_DROPPED
  14. && isStaleEventLocked(currentTime, typedEntry)) {
  15. dropReason = DROP_REASON_STALE;
  16. }
  17. if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
  18. dropReason = DROP_REASON_BLOCKED;
  19. }
  20. done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
  21. break;
  22. }
  23. ...
  24. }
=>
dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)
=>
  1. CommandEntry* commandEntry = postCommandLocked(
  2. & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
=>
 nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);
又回到了问题的起点,interceptKeyBeforDispatching。我想到了我几年前画的一草图,其实当时并未完全理解,现在再看,只是信息更丰富了。
InputReader通过EventHub轮询读取底层数据,处理后,会通过Queue给到InputDispacher,InputDispacher再做进一步策略处理,发送给View做显示。
有了以上的信息,我们可以回答,一个Key键值的上报是先通过interceptKeyBeforQueueing 再执行interceptKeyBeforDispatch。
input之我见03——InputMangerService(Android4.4)

input之我见03——InputMangerService(Android4.4)

继续上一讲,从InputManagerService讲起,自上而下分析。
Android中管理Input的两个主要相关角色, 一是WindowManagerService, 一是跟View相关的ViewRootImpl. 基本原理方向从2.3到目前的4.2都一样,在  Android app一启动之后, ViewRootImpl 就会先跟 WindowManagerService 建立inputChannel, 一旦 WindowManagerService 有收到 event 就会经由 inputChannel 通知 ViewRootImpl 去共享内存中抓取 event. 虽然方向一样, 但是里面的架构有改,目前最新的版本是android 4.2, 所以以下的输入事件处理程序是以4.2来说明, 以后的版本一定会再更改.到时候在研究
0,和input相关的几个目录(4.4为例)
frameworks/base/core/java/android/hardware/input     -> class InputManager //api
IInputDevicesChangedListener.aidl  IInputManager.aidl  InputManager.java  KeyboardLayout.aidl  KeyboardLayout.java
Class Overview
Provides information about input devices and available key layouts.
Get an instance of this class by calling Context.getSystemService() with the argument INPUT_SERVICE.
frameworks/base/services/java/com/android/server/input     -> InputMangerService 服务。system/framework/services.jar  
          (InputApplicationHandle.java  InputManagerService.java  InputWindowHandle.java  PersistentDataStore.java)

framework/base/services/input/      -> /system/lib/libinputserver.so
frameworks/native/libs/input          ->/system/lib/libinput.so     //character map
frameworks/base/cmds/input/         -> /system/bin/input     //input command
1,InputMangerService是怎么产生的?
base/services/java/com/android/server/SystemServer.java
 329             Slog.i(TAG, “Input Manager”);                                                                                                                
330             inputManager = new InputManagerService(context, wmHandler);
InputManagerService的代码位于
frameworks/base/services/java/com/android/server/input/InputManagerService.java
2,
一开机的时候, SystemServer 会启动 InputManagerService, 这时 InputManagerService 又会去启动两个Thread,InputReaderThread,InputDispatcherThread和一个 EventHubInterface 
{
如何来生成两个Thread?
InputManagerSerivice.java中的构造函数
InputManagerService()
->
nativeInit()
->
com_android_server_input_InputManagerService.cpp
nativeInit()
->
new NativeInputManager()
->
new InputManager()
->
framework/native/service/inputflinger(androidL)
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();
}
=>
void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
—-
以InputReaderThread为例
InputReader.cpp
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}

InputReaderThread::~InputReaderThread() {
}

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();//这里的mReader就是InputReader的实例
    return true;
}

->
void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//EventHubInterface,getEvents就是读driver的/dev目录
}
其中,这个线程有一个非常重要的函数getEvents,就是这个线程主要的工作
getEvents(){
for(; 😉
{
readNotifyLocked()
}
}
会不停的读系统输入设备的状态。

}

因此输入事件处理流程跟此三位角色有关系.InputReaderThreadEventHubInterface抓取新的inputevent, 然后在依各个的eventtype 进入各个的 event mapper,  各个的 event mapper 处理输入事件完之后,InputReaderThread 将new input 放进 Queue 中. 之后 InputDispatcherThread再从Queue中取出Input Event 放入共享内存中. 此时再通知 View 去共享内存抓取 new Input Event, 取完之后再通知InputDispatcherThread是否可以再放新的InputEvent到共享内存中, 
这段话这样解释。
input之我见02——Input调试Tips

input之我见02——Input调试Tips

在kernel对Input有了一定的认识之后,就开始Android的Input之旅,先不急着深挖代码,来看看如何获得Input的相关打印信息。
1,根据事件路由,打开各个节点debug信息
  • EventHub.cpp

KeyWord:

#define LOG_TAG “EventHub”
#define LOG_NDEBUG 0
KeyFunction:
getEvents()
  • InputReader.cpp
KeyWord:
#define LOG_TAG “InputReader”
#define LOG_NDEBUG 0
KeyFunction:
loopOnce()

  • InputDispatcher()
KeyWord:
#define LOG_TAG “InputDispatcher”
#define LOG_NDEBUG 0
#define DEBUG_INBOUND_EVENT_DETAILS 1
#define DEBUG_OUTBOUND_EVENT_DETAILS 2
KeyFunction:
notifyKey()
示例:
logcat -s “EventHub” “InputReader” “InputDispatcher” 
  1. 01-01 00:00:21.734 4076 4114 V EventHub: Opening device: /dev/input/event2
  2. 01-01 00:00:21.734 4076 4114 V EventHub: Created descriptor: raw=:1b8e:0cec:, cooked=02b1ce58713e0e531eab61b0742baac6893ff49f
  3. 01-01 00:00:21.734 4076 4114 V EventHub: add device 1: /dev/input/event2
  4. 01-01 00:00:21.734 4076 4114 V EventHub: bus: 0010
  5. 01-01 00:00:21.734 4076 4114 V EventHub: vendor 1b8e
  6. 01-01 00:00:21.734 4076 4114 V EventHub: product 0cec
  7. 01-01 00:00:21.734 4076 4114 V EventHub: version 0001
  8. 01-01 00:00:21.734 4076 4114 V EventHub: name: "cec_input"
  9. 01-01 00:00:21.734 4076 4114 V EventHub: location: ""
  10. 01-01 00:00:21.734 4076 4114 V EventHub: unique id: ""
  11. 01-01 00:00:21.734 4076 4114 V EventHub: descriptor: "02b1ce58713e0e531eab61b0742baac6893ff49f"
  12. 01-01 00:00:21.734 4076 4114 V EventHub: driver: v1.0.1
  13. 01-01 00:00:21.735 4076 4114 D EventHub: No input device configuration file found for device 'cec_input'.
  14. 01-01 00:00:21.741 4076 4114 W EventHub: Unable to disable kernel key repeat for /dev/input/event2: Function not implemented
  15. 01-01 00:00:21.741 4076 4114 I EventHub: New device: id=1, fd=88, path='/dev/input/event2', name='cec_input', classes=0x1, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
  16. 01-01 00:00:21.741 4076 4114 V EventHub: Opening device: /dev/input/mice
  17. 01-01 00:00:21.741 4076 4114 E EventHub: could not get driver version for /dev/input/mice, Not a typewriter
  18. 01-01 00:00:21.794 4076 4114 V EventHub: Opening device: /dev/input/event1
  19. 01-01 00:00:21.795 4076 4114 V EventHub: Created descriptor: raw=:0001:0001:, cooked=485d69228e24f5e46da1598745890b214130dbc4
  20. 01-01 00:00:21.795 4076 4114 V EventHub: add device 2: /dev/input/event1
  21. 01-01 00:00:21.795 4076 4114 V EventHub: bus: 0010
  22. 01-01 00:00:21.795 4076 4114 V EventHub: vendor 0001
  23. 01-01 00:00:21.795 4076 4114 V EventHub: product 0001
  24. 01-01 00:00:21.795 4076 4114 V EventHub: version 0100
  25. 01-01 00:00:21.795 4076 4114 V EventHub: name: "gpio_keypad"
  26. 01-01 00:00:21.795 4076 4114 V EventHub: location: "gpio_keypad/input0"
  27. 01-01 00:00:21.795 4076 4114 V EventHub: unique id: ""
  28. 01-01 00:00:21.795 4076 4114 V EventHub: descriptor: "485d69228e24f5e46da1598745890b214130dbc4"
  29. 01-01 00:00:21.795 4076 4114 V EventHub: driver: v1.0.1
  30. 01-01 00:00:21.795 4076 4114 D EventHub: No input device configuration file found for device 'gpio_keypad'.
  31. 01-01 00:00:21.800 4076 4114 I EventHub: New device: id=2, fd=89, path='/dev/input/event1', name='gpio_keypad', classes=0x1, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
  32. 01-01 00:00:21.800 4076 4114 V EventHub: Opening device: /dev/input/event0
  33. 01-01 00:00:21.801 4076 4114 V EventHub: Created descriptor: raw=:0001:0001:nonce:0001, cooked=d2c52ff0f656fac4cd7b7a118d575e0109a9fe1c
  34. 01-01 00:00:21.801 4076 4114 V EventHub: add device 3: /dev/input/event0
  35. 01-01 00:00:21.801 4076 4114 V EventHub: bus: 0010
  36. 01-01 00:00:21.801 4076 4114 V EventHub: vendor 0001
  37. 01-01 00:00:21.801 4076 4114 V EventHub: product 0001
  38. 01-01 00:00:21.801 4076 4114 V EventHub: version 0100
  39. 01-01 00:00:21.801 4076 4114 V EventHub: name: "aml_keypad"
  40. 01-01 00:00:21.801 4076 4114 V EventHub: location: "keypad/input0"
  41. 01-01 00:00:21.801 4076 4114 V EventHub: unique id: ""
  42. 01-01 00:00:21.801 4076 4114 V EventHub: descriptor: "d2c52ff0f656fac4cd7b7a118d575e0109a9fe1c"
  43. 01-01 00:00:21.801 4076 4114 V EventHub: driver: v1.0.1
  44. 01-01 00:00:21.801 4076 4114 D EventHub: No input device configuration file found for device 'aml_keypad'.
  45. 01-01 00:00:21.805 4076 4114 W EventHub: Unable to disable kernel key repeat for /dev/input/event0: Function not implemented
  46. 01-01 00:00:21.805 4076 4114 I EventHub: New device: id=3, fd=90, path='/dev/input/event0', name='aml_keypad', classes=0x863, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
  47. 01-01 00:00:21.805 4076 4114 V EventHub: Created descriptor: raw=:0000:0000:uniqueId:<virtual>name:Virtual, cooked=a718a782d34bc767f4689c232d64d527998ea7fd
  48. 01-01 00:00:21.810 4076 4114 V EventHub: Reporting device opened: id=-1, name=<virtual>
  49. 01-01 00:00:21.810 4076 4114 V EventHub: Reporting device opened: id=3, name=/dev/input/event0
  50. 01-01 00:00:21.810 4076 4114 V EventHub: Reporting device opened: id=2, name=/dev/input/event1
  51. 01-01 00:00:21.810 4076 4114 V EventHub: Reporting device opened: id=1, name=/dev/input/event2
  52. 01-01 00:00:21.810 4076 4114 I InputReader: Device added: id=-1, name='Virtual', sources=0x00000301
  53. 01-01 00:00:21.810 4076 4114 I InputReader: Device added: id=3, name='aml_keypad', sources=0x00004703
  54. 01-01 00:00:21.811 4076 4114 I InputReader: Reconfiguring input devices. changes=0x00000080
  55. 01-01 00:00:21.811 4076 4114 I InputReader: Device added: id=2, name='gpio_keypad', sources=0x00000101
  56. 01-01 00:00:21.811 4076 4114 I InputReader: Device added: id=1, name='cec_input', sources=0x00000101
  57. 01-01 00:00:21.811 4076 4114 D InputDispatcher: notifyDeviceReset - eventTime=16199233173, deviceId=-1
  58. 01-01 00:00:21.811 4076 4114 D InputDispatcher: notifyDeviceReset - eventTime=16199233173, deviceId=3
  59. 01-01 00:00:21.811 4076 4114 D InputDispatcher: notifyDeviceReset - eventTime=16199233173, deviceId=2
  60. 01-01 00:00:21.811 4076 4113 D InputDispatcher: dispatchDeviceReset - eventTime=16199233173, deviceId=-1
  61. 01-01 00:00:21.811 4076 4113 D InputDispatcher: dispatchDeviceReset - eventTime=16199233173, deviceId=3
  62. 01-01 00:00:21.811 4076 4114 D InputDispatcher: notifyDeviceReset - eventTime=16199233173, deviceId=1
  63. 01-01 00:00:21.811 4076 4113 D InputDispatcher: dispatchDeviceReset - eventTime=16199233173, deviceId=2
  64. 01-01 00:00:21.811 4076 4114 D InputDispatcher: notifyConfigurationChanged - eventTime=16199233173
  65. 01-01 00:00:21.811 4076 4113 D InputDispatcher: dispatchDeviceReset - eventTime=16199233173, deviceId=1
  66. 01-01 00:00:21.812 4076 4113 D InputDispatcher: dispatchConfigurationChanged - eventTime=16199233173
2,KeyLayout文件加载debug
framework/native/libs/input/InputDevice.cpp
#define LOG_TAG “InputDevice”
+#define DEBUG_PROBE 1
+#define LOG_NDEBUG 0
framework/native/libs/input/KeyLayoutMap.cpp
#define LOG_TAG “KeyLayoutMap”
#define LOG_NDEBUG 0
#define DEBUG_PARSER 1
#define DEBUG_MAPPING 1
示例:
logcat -s “EventHub” “InputDevice” “KeyLayoutMap
3,dumpsys input
读取Input相关的信息,大概分为Event Hub State、Input Reader State、Input Dispatcher State这三个部分,从这里,同时也初窥到了Android中Input的层次关系
Input之我见01——设备驱动初探

Input之我见01——设备驱动初探

在我们日常接触的计算机模型中,可以抽象成输入模块、输出模块和运算单元。随着技术不断发展革新,输入、输出设备都有着不同程度的发展。尤其是输入设备,让人机交互有了很大变化。在Android系统中,对于输出设备事件是处理是基本linux的输入子系统,同时也有了自己的特征。我试图通过自己整理资料、研究代码、调试跟踪来理清之间的脉络,同时也是Android系统一个从底层到上层的脉络。由于某些方面的局限,也许无法能解决你心中所有疑问,希望对你有所启发。如果文中有不错误疏漏的,也欢迎你提意见。
该小节先讲Linux Input子系统的内容

先看一下Linux Input子系统架构图

在Kernel里面主要分三层,设备驱动层、Input Core、事件驱动层。

相关数据结构

  • 静态数据结构
存储Input设备的信息,主要位于input.h
  1. struct input_id {
  2. __u16 bustype;
  3. __u16 vendor;
  4. __u16 product;
  5. __u16 version;
  6. };
input_event用于存储每一次传输的数据,这是个固定大小的结构体。
  1. struct input_event {
  2. struct timeval time;
  3. __u16 type;
  4. __u16 code;
  5. __s32 value;
  6. };
其中type定义在Kernel(3.14)的Input子系统中,定义如下18种Input事件,常用的也就是前面5,6种
  1. /* Events */
  2. #define EV_SYN 0x00 //用于同步
  3. #define EV_KEY 0x01 //用于按键,遥控器等,键值的输入设备
  4. #define EV_REL 0x02 //相对事件,用于鼠标的输入
  5. #define EV_ABS 0x03 //绝对事件,用于触摸屏
  6. #define EV_MSC 0x04
  7. #define EV_LED 0x11
  8. #define EV_SND 0x12
  9. #define EV_REP 0x14
  10. #define EV_FF 0x15
  11. #define EV_PWR 0x16
  12. #define EV_FF_STATUS 0x17
  13. #define EV_MAX 0x1f
在Android7.0的EventHub中,对底层的事件类型,作了自己的分类,但有相应的对应关系
  1. EventHub.h
  2. /*
  3. * Input device classes.
  4. */
  5. enum {
  6. /* The input device is a keyboard or has buttons. */
  7. INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
  8. /* The input device is an alpha-numeric keyboard (not just a dial pad). */
  9. INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
  10. /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
  11. INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
  12. ...
  13. };

  • 动态数据结构
在Input事件驱动层,以dvdev为例
input_handler:input管理者
  1. struct input_handler {
  2. void *private;
  3. void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  4. bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  5. bool (*match)(struct input_handler *handler, struct input_dev *dev);
  6. int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
  7. void (*disconnect)(struct input_handle *handle);
  8. void (*start)(struct input_handle *handle);
  9. ......
  10. struct list_head h_list;
  11. struct list_head node;
  12. };
input_handle:input设备的句柄
  1. struct input_handle {
  2. void *private;
  3. int open;
  4. const char *name;
  5. struct input_dev *dev;
  6. struct input_handler *handler;
  7. struct list_head d_node;
  8. struct list_head h_node;
  9. };
input_dev:input设备
  1. struct input_dev {
  2. const char *name;
  3. const char *phys;
  4. const char *uniq;
  5. struct input_id id;
  6. unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
  7. unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
  8. unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
  9. ....
  10. int (*setkeycode)(struct input_dev *dev,
  11. const struct input_keymap_entry *ke,
  12. unsigned int *old_keycode);
  13. int (*getkeycode)(struct input_dev *dev,
  14. struct input_keymap_entry *ke);
  15. ...
  16. unsigned long key[BITS_TO_LONGS(KEY_CNT)];
  17. unsigned long led[BITS_TO_LONGS(LED_CNT)];
  18. unsigned long snd[BITS_TO_LONGS(SND_CNT)];
  19. unsigned long sw[BITS_TO_LONGS(SW_CNT)];
  20. int (*open)(struct input_dev *dev);
  21. void (*close)(struct input_dev *dev);
  22. int (*flush)(struct input_dev *dev, struct file *file);
  23. int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
  24. struct input_handle __rcu *grab;
  25. ...
  26. struct device dev;
  27. struct list_head h_list;
  28. struct list_head node;
  29. };
这三者Input_dev,hander,与Hande的关系
应该很清楚了吧,这里只以其中的一个input_dev和handler为例画出来的图,其他的以此类推。其中input_dev_list和input_handler_list是两条全局的链表,每当调用input_register_device函数时会将Input设备加入到input_dev_list链表的尾部;每当调用input_register_handler函数时会将handler加入到input_handler_list链表的尾部;每当调用input_register_handle函数时会将handle加入到其对应的Input设备的h_list链表的尾部,并且还会该handle加入到其对应的handler的h_list链表的尾部。

Input设备驱动

总共有三个核心的函数
input_register_handler(&evdev_handler);
input_register_handle(&evdev->handle);
input_register_device(remote->input);
前面两个是在事件驱动层的evdev.c的init函数和connect函数中调用;最后一个是在设备驱动中注册的。
我们从最底层的input_register_device分析起,以IR遥控器驱动为例
在probe函数中,进行input_register_device把该input设备注册进全局的input_dev_list链表
  1. input_register_device(remote->input);
=》
  1. int input_register_device(struct input_dev *dev)
  2. {
  3. ...
  4. //添加到全局设备
  5. error = device_add(&dev->dev);
  6. ...
  7. //添加到设备链表
  8. list_add_tail(&dev->node, &input_dev_list);
  9. //遍历input_handler_list链表,以node为关键字,把dev加入到handler_list中
  10. list_for_each_entry(handler, &input_handler_list, node)
  11. input_attach_handler(dev, handler);
在input_attach_handler中,
  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
  2. {
  3. id = input_match_device(handler, dev);
  4. if (!id)
  5. return -ENODEV;
  6. ...
  7. error = handler->connect(handler, dev, id);
  8. ...
  9. return error;
  10. }
通过id进行匹配,第一次的话hander链表中没有dev对应的handler,就进行connect,其实是调用input_register_handle()。把input_dev,input_handler,input_handle关联起来。
注册完成之后,一般就调用这两个函数发送数据到input子系统中
  1. input_event(dev, EV_KEY, scancode, type);
  2. input_sync(dev);
主要看input_event
=》
input_handle_event(dev, type, code, value);
=》
input_pass_event(dev, type, code, value);
=》
handle->handler->event(handle, type, code, value);
在input_register_handler的时候,为dev分配了一个handler,并与handle进行关联了,此时就直接进入event函数了。
  1. static void evdev_event(struct input_handle *handle,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. struct evdev *evdev = handle->private;
  5. struct evdev_client *client;
  6. struct input_event event;
  7. event.type = type;
  8. event.code = code;
  9. event.value = value;
  10. ...
  11. client = rcu_dereference(evdev->grab);
  12. if (client)
  13. evdev_pass_event(client, &event);
  14. else
  15. list_for_each_entry_rcu(client, &evdev->client_list, node)
  16. evdev_pass_event(client, &event);
  17. ...
  18. if (type == EV_SYN && code == SYN_REPORT)
  19. wake_up_interruptible(&evdev->wait);
  20. }
把事件通过evdev_pass_event传递给client,如果是同步事件的话,就调用wait,唤醒相应的监听进行。
TOP