一、EventHub简述

Android系统基于Linux系统,由多个子系统组合而成,各子系统分工合作,在各自功能域中扮演关键角色。其中一个比较重要的子系统是Input子系统,正如其名地,挂载于Android的各输入设备的输入事件,会通过Input子系统传输到上层(Android框架层或事件处理层)执行处理流程。

EventHub在Input子系统中可以看做起到连结上下层的一个重要模块:对下,它监听Input设备的加入与删除,获取Input设备的输入事件;往上,它将输入事件整合由InputReader获取并分发至各Android上层服务或应用。简单来说,EventHub的工作原理是,InputReader中持续循环线程loopOnce()调用EventHub的getEvents(),该函数epoll_wait等待着Input设备的输入事件到来。EventHub的工作又可以细分为监听新设备加入与监听设备事件获取两部分。

1、新设备加入

(1)/dev/input/路径下有新设备加入

(2)该路径受mINotifyFd监听,设备加入后得到IN_CREATE事件

(3)mINotifyFd受mEpollFd监听,得知获得新事件,mEpollFd从epoll_wait跳出

(4)得知事件来自mINotifyFd,mPendingINotify设true,下一轮触发readNotifyLocked()

(5)调用openDeviceLocked()打开设备,包括配置identifier、classes、keymap等

(6)将设备交由mEpollFd监听,并加入mDevices与mOpeningDevices

(7)下一轮发现mOpeningDevices里有设备,建立事件

504e9099e46a45decd2f3fb2a4875d96.png

2、设备事件获取

(1)设备来了新事件

(2)设备在加入时都加入mEpollFd监听,此时mEpollFd得知有新事件,从epoll_wait跳出

(3)Device*device = getDeviceByFdLocked(eventItem.data.fd)根据事件的fd定位设备

(4)read(device->fd,readBuffer, sizeof(struct input_event) * capacity)——从设备读取输入事件

     count = size_t(readSize) / sizeof(structinput_event)——计算事件的数量

(5)轮询解析事件参数

bc7a4964b5e6a9c1eec068b7e3c8dad8.png

上述流程中,event+=1后,event != buffer ,此时说明有新事件产生,退出getEvents(),新事件返回至调用者InputReader,待处理完毕后再次进入getEvents()。

EventHub的详细流程图如下:

ee6204be5aeff657d1cf260189073963.png

二、EventHub():构造函数

EventHub的构造函数通过注册INotify与Epoll监听设备的增删事件及输入事件。INotify作为文件系统变化通知机制,可以监控文件系统的变化,包括Input设备文件节点的新建、删除、读写等等,当被监听的事件发生时,可以通过read()函数从inotify对象中将事件信息读取出来。Epoll可以使用一次等待监听多个描述符的可读可写状态。

构造函数的代码流程为

(1)各种变量初始化

(2)创建epoll对象与inotify对象

da350229ab38473109ed273b2d2ab2bc.png

(3)将mINotifyFd作为mEpollFd的一个监控对象

62bfa27c946726ca824e796f6c52e139.png

(4)创建唤醒pipe,EventHub唤醒时(wake()),往writepipe写值,readpipe受mEpollFd监控 

2da57e20adbe4fa09946fe3ee37a35cf.png

三、getEvents():输入设备事件的监听与读取

getEvents()于InputReader的循环函数中调用,其实质也是个死循环,由循环末端的epoll_wait执行等待。每当有新的输入事件到来时,函数会从等待中跳出,根据事件的类型执行相应的处理,并通过buffer参数更新事件存储结构体RawEvent;处理完毕后重新进入等待,如此反复。

55aee4fbf07fdbdb2c2857ccac4ed3bc.png

它处理的事件有如下类型:

1.设备关闭:遍历所有待关闭设备逐一移除

9bf9acd2dc9fb1a759bb769428d78899.png

2.设备重打开:先关闭所有设备,进入下次循环扫描所有可用输入设备

82c8842f0cf629a9e0cb1a7edc93f1e0.png

3.设备扫描与打开

(1)scanDevicesLocked()扫描所有可用输入设备,加入Device结构体

5d170cc2e58ed13ff8a820d48f910f77.png

(2)扫描发现处于打开中状态的设备,遍历设备,创建DEVICE_ADDED事件

adf2d93ca846613f6566ec4b7c1fb2e3.png

(3)扫描结束,创建FINISHED_DEVICE_SCAN事件

dd704b73715bdc4ea17bc85a9f366bb9.png

4.Input事件处理

(1)遍历所有待处理事件

a00e89d451d1ec0e996dd3b5afe42289.png

(2)通过epoll事件的data字段确认事件来源,看是否EventHub自身注册的INotify事件或wake唤醒事件

0e4fd04f66d5310c2dff26e9fceace67.png

(3)如非上述EventHub事件,getDeviceByFdLocked()获知事件的设备来源,对输入事件执行处理。事件为EPOLLIN类型时,从读fd取输入事件,轮询各input_event结构体数据,转换为RawEvent对象,通过buffer参数返回调用者InputReader

1fbece40080f97aaaa4546005e8ea468.png

事件为EPOLLHUP类型时,表示设备节点已挂断(hung up),关闭设备。

836d52c9777498be22c6abb1e0f1b581.png

5.在上面的处理中,如果事件是来自INotify的EPOLLIN,说明设备节点发生了增删操作,调用readNotifyLocked()读取事件,完成设备的加载与卸载。

979b321da606cd375d36f85e6f4c5b83.png

6.事件处理完毕,进入epoll_wait,等待新事件

3a325f0a9cdec67ba5a51a34f622804b.png

四、scanDevicesLocked() & openDeviceLocked():可用设备扫描及加载

d5279dd19c4a49b899f852ed0bf7de92.png

1. scanDevicesLocked()

通过scanDirLocked()扫描目录DEVICE_PATH(/dev/input)下的所有设备,调用 openDeviceLocked()全部打开

9b8fef8a24b9d924316c424352aea0c7.png

66f59d03e08ded06cf242233a6743a30.png

2. openDeviceLocked()

根据上面扫描到的设备路径打开设备节点,获取输入设备的InputDeviceIdentifier结构体,其中包括如下信息

c04d585ac4215d40a619bb5e61849013.png

输入设备注册时会传入Bitmask,根据Bitmask可将输入设备分为以下类型:

0930e5f205c19962eeffba310da858a9.png

输入设备类型的不同,设备的配置会有一定的区别,此处不展开说明,可参照上面的流程图。

在设备加载的最后阶段,都会统一调用registerDeviceForEpollLocked(device)注册设备,并addDeviceLocked()将设备加入至设备列表。

919b2c57973727f00363c1e49b5ea024.png

而registerDeviceForEpollLocked()的本质是将设备fd加入epoll监控。

254dad66424e4b364261f2ac0a9db0d2.png

另外,在上面getEvents()流程中,提及收到INotify事件时会执行readNotifyLocked()函数,完成设备的加载与卸载,此处的加载流程是也是通过调用openDeviceLocked()实现的;而卸载流程调用的closeDeviceLocked()实现逻辑更为简单:

772035cf7e672932a4385391519d1d98.png

五、小结

EventHub虽然代码简单,但它是Android输入子系统必不可少的一环,是连结底层输入设备与上层InputReader的纽带,了解EventHub的工作原理对了解Android输入子系统很有帮助。

参考资料:

1、《深入理解Android 卷III》

2、https://android.googlesource.com/platform/frameworks/native/+/refs/heads/master/services/inputflinger/reader/EventHub.cpp

07658bb9ddfa6b7802a9e4b7c8576864.gif

长按关注内核工匠微信


Linux 内核黑科技 | 技术文章 | 精选教程
Logo

更多推荐