在前面的学习中,我们分析了android输入子系统的框架,知道他会存在Reader与Dispatcher两个线程,Reader是循环读取驱动事件,并且监测是否有输入设备拔插,我们可以想象一下Reader线程会做哪些事情,我们可以简单的认为他是获取事件,然后把这个事件发送给Dispatcher,其中还包括了一些简单的处理。简单的框图如下: 再次粘贴一下上小节的总框图
在右下角我们可以看到如下
可以知道最终调用了mReader->loopOnce,其处于inputReader.cpp文件,打开该文件,找到loopOnce函数,
void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //获取事件
if (count) {
processEventsLocked(mEventBuffer, count); //处理事件
mQueuedListener->flush();//把这些事件刷送给Dispatcher线程
接下来的几个小节将围绕
1.Reader线程_使用EventHub读取事件
2.Reader线程_处理事件
3.Reader线程_送Dispatcher线程发送事件
下面我们开始:mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);,该函数获取的事件存放在mEventBuffer中,其为一个RawEvent mEventBuffer[EVENT_BUFFER_SIZE]数组,并且RawEvent为:
struct RawEvent {
nsecs_t when;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
};
其中的int32_t type可以取值如下: 在之前的小节中,做了一个模拟的输入设备,其上报的类型为input_event区中程序
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
把上面的的内容整理成框图如下: 红线部分代表应用层,红线下方代表驱动层,在应用层,InputReader.cpp中通过mEventHub->getEvents获取的是struct RawEvent类型,驱动获取上报的为struct input_event,可以知道在mEventHub->getEvents中,会把驱动上报的input_event构造成RawEvent,那么mEventHub是怎么检测输入设备的拔插的呢?在前面介绍过inotify和epoll_P,通过对目录或者文件的监测,实现热插拔,这里也是利用了同样的原理: 1.使用inotify监测/dev/input目录。 2.使用epoll监测有无数据。 当有设备接入的时候,mEventHub->getEvents就会构造一个RawEvent其中type为DEVICE_ADDED 当有设备一出的时候,mEventHub->getEvents就会构造一个RawEvent其中type为DEVICE_REMOVE 如果真的又数据可读,进行了上报,那么他就会构造其具体的input_event事件,与驱动一一对应,在驱动层是没有办法实现热插拔的,所以把驱动层传来的input_event进行了少许修改,从而实现热插拔。
下面我们开始阅读以下源代码:在InputReader.cpp文件中,找到之前的mEventHub->getEvents,那么mEventHub是什么?查看其定义sp mEventHub,可知道他是EventHub类型。我们打开EventHub.cpp,查看其构造函数:
EventHub::EventHub(void) :
mEpollFd = epoll_create(EPOLL_SIZE_HINT);//创建mEpollFd
mINotifyFd = inotify_init();
/*监测DEVICE_PATH = "/dev/input"目录*/
inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
我们再查看mEventHub->getEvents,根据前面的分析,他应该去扫描"/dev/input"目录下的每一个节点。
EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
for (;;) {
ALOG_ASSERT(bufferSize >= 1);
if (mNeedToScanDevices) {
scanDevicesLocked(); //扫描目录
scanDirLocked(DEVICE_PATH); //扫描目录
while((de = readdir(dir))) { //读取目录
openDeviceLocked(devname);//处理其中每一个设备节点
open(devicePath, O_RDWR | O_CLOEXEC);//打开设备文件
ioctrl()....//调用一系列的ioctrl(),稍后再进行分析
ioctrl()....
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {//添加到epoll中进行监测,
pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//等待驱动程序响应,或者等待监测文件动作
我们回到void InputReader::loopOnce() 中,既然他调用epoll_ctl,对那些设备进行了监测,那么他肯定会调用epoll_wait,等待驱动程序响应。当响应之后,该循环完成一次,可以想象,当增加或者删除了一个节点,在这个循环中,肯定做了对应的处理:
EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
for (;;) {
while (mPendingEventIndex = mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
read(mINotifyFd, event_buf, sizeof(event_buf));//读取这个目录
while(res >= (int)sizeof(*event)) {
if(event->mask & IN_CREATE) {//判断是删除还是增加了设备,做出相应动作。
openDeviceLocked(devname);//如果创建设备,打开该设备,加入epoll
else
closeDeviceByPathLocked(devname);//如果删除设备,则从epoll中移除该设备
deviceChanged = true;
}
我们再次重新回到void InputReader::loopOnce() 中,如果没有设备节点增加或者删除,那么就是有数据来了:
EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
for (;;) {
deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
readSize = read(device->fd, readBuffer,sizeof(struct input_event) * capacity);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
我们可以看到,把数据读取到了struct input_event中,则就是内核中原始的上报的数据。然后构造成一个RawEvent* event。 这样我们就分析完成了。其逻辑如下: