上篇文章《Android6.0输入系统之InputManagerService构成分析》 完成了IMS的创建,接着就沿着输入系统这条路继续往下走。
在EventHub的构造函数中,它通过INotify与Epoll机制建立起对设备点增删事件及可读状态的监听。INotify是Linux内核所提供的一种文件系统变化通知机制。它可以为应用程序监控文件系统的变化,如文件的新建、删除、读写等等。它有两个基本对象,inotify对象对应一个队列,应用程序可以向inotify对象添加多个监听,当被监听的事件发生时,可以通过read()函数从inotify对象中将事件信息读取出来;而watch对象则用来描述文件系统的变化事件的监听,它是一个二元组,包括监听目标和事件掩码两个元素,监听目标是文件系统的一个路径,可以是文件也可以是文件夹。Epoll可以使用一次等待监听多个描述符的可读/可写状态。
frameworks/native/services/inputflinger/EventHub.cpp
设备节点监听的建立
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
//1.首先使用epoll_create()函数创建一个epoll对象
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
...
//2.创建一个inotify对象,用来监听设备节点的增删事件
mINotifyFd = inotify_init();
//将存储设备点的路径dev/input作为监听对象添加到inotify对象中
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
//3.将mINotifyFd作为epoll的一个监控对象
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN; //监听mINotifyFd可读
eventItem.data.u32 = EPOLL_ID_INOTIFY;
//将对mINotifyFd的监听注册到epoll对象中
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
...
}
getEvents()函数的工作方式、输入设备管理、原始输入事件的监听与读取
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
/* event指针指向在buffer中下一个可用于存储事件的RawEvent结构体。每存储一个事件,event指针都会向后偏移一个元素 */
RawEvent* event = buffer;
//capacity记录了buffer中的剩余的元素数量
size_t capacity = bufferSize;
bool awoken = false;
//循环getEvent()函数的主体
for (;;) {
...
//遍历mClosingDevices链表,为每一个已卸载的设备生成DEVICE_REMOVED事件
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now; //设置产生事件的事件戳
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
//设置事件的类型为DEVICE_REMOVED
event->type = DEVICE_REMOVED;
//将event指针移动到下一个可用于填充事件的RawEvent
event += 1;
//生成DEVICE_REMOVED事件之后,被卸载的Device对象就不再需要了
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
//scanDevicesLocked()将会把dev/input下所有可用的输入设备打开并储存到Device结构体中
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
//处理未被inputReader取走的输入事件与设备事件
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
/* 通过Epoll事件的data字段确定此事件表示mINotifyFd可读注意EPOLL_ID_INOTIFY在EventHub的构造函数中作为data字段向Epoll注册mINotifyFd的可读事件*/
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true; //标记INotify事件待处理
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue; //继续处理下一条Epoll事件
}
//其他Epoll事件的处理
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
//通过Epoll的data.u32字段获取设备Id,进而获取对应的Device对象
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0) {
ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
eventItem.events, eventItem.data.u32);
continue;
}
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
//如果Epoll事件为EPOLLIN,表示设备节点有原始输入事件可读
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
//将读取到的每一个input_event结构体中的数据转换为RawEvent对象,并存储在buffer参数中以返回给调用者
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
...
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
...
//若mINotifyFd有数据可读,说明该设备节点发生了增删操作
//若INotify事件待处理
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
/* 调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件这个函数将完成设备的加载与卸载 */
readNotifyLocked();
deviceChanged = true;
}
//设备节点增删操作发生时,则重新执行循环体,以便将设备变化的事件放入buffer中
//若处理了INotify事件,则返回到循环开始处,生成设备增删事件
if (deviceChanged) {
continue;
}
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
//若此次getEvents()调用没能获取事件,说明mPendingEventIndex中没有事件可用。
mPendingEventIndex = 0;
...
//返回本次getEvents()调用所读的事件数据
return event - buffer;
}
加载所有输入设备由scanDevicesLocked()函数完成,代码如下:
void EventHub::scanDevicesLocked() {
//调用scanDirLocked()函数遍历dev/input文件夹下的所有设备节点并打开
status_t res = scanDirLocked(DEVICE_PATH);
if(res < 0) {
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
}
//时刻打开这一个名为VIRTUAL_KEYBOARD虚拟的输入设备
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
}
}
scanDevicesLocked()遍历指定文件夹下的所有设备节点,分别对其执行openDeviceLocked()完成设备的打开操作,为设备节点创建并加载Device结构体。代码如下:
status_t EventHub::openDeviceLocked(const char *devicePath) {
...
//打开设备节点的文件描述符,用于获取设备信息以及读取原始输入事件
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
//通过ioctl()函数从设备节点中获取输入设备的厂商信息
InputDeviceIdentifier identifier;
...
//分配一个设备id并创建Device结构体
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
...
//为设备加载配置信息
loadConfigurationLocked(device);
// 通过ioctl函数获取设备的事件位掩码
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
...
//将设备节点的描述符的可读事件注册到Epoll中。
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
if (mUsingEpollWakeup) {
eventItem.events |= EPOLLWAKEUP;
}
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
...
//调用addDeviceLocked()将Device添加到mDevices字典中
addDeviceLocked(device);
return 0;
}
调用readNotifyLocked()函数读取并处理存储在mINotifyFd中的INotify事件这个函数将完成设备的加载与卸载。代码如下:
status_t EventHub::readNotifyLocked() {
...
//从mINotifyFd中读取INotify事件列表
res = read(mINotifyFd, event_buf, sizeof(event_buf));
...
//逐个处理列表中的事件
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
strcpy(filename, event->name); //从事件中获取设备节点路径
if(event->mask & IN_CREATE) {
openDeviceLocked(devname); //若事件类型为IN_CREATE,则加载对应的设备
} else {
ALOGI("Removing device '%s' due to inotify event\n", devname);
closeDeviceByPathLocked(devname); //否则卸载对应设备
}
}
//移动到列表中的下一个事件
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
return 0;
}
EventHub作为直接操作设备节点的输入系统组件,通过接口getEvent()向使用者提供抽取设备事件与原始输入事件的功能,其核心都是在getEvent()函数中完成的。下面接着讨论EventHub所提供的原始输入事件如何被加工为Android输入事件——InputReader。