《原理分析(一)》中简单分析了IMS 的启动过程,大致如下:
- 通过IMS的构造,创建InputManager 实例,并保存到新建的实例NativeInputManager 中。InputManager实例时分别创建InputDispatcher 和InputReader;
- 将IMS 的实例传入WSM 中;
- 将WSM 中input 相关的callback 注册到IMS,方便后面交互;
- 开启IMS 的启动流程,分别创建Dispatch thread 和read thread。
在分析input 的分发原理前,先来了解所有input 设备event 监听器EventHub。
1. EventHub 构造
构造函数是在create input reader时调用,并将实例作为一个shared_ptr 保存在InputReader 中。下面来详细分析EventHub 构造过程。
frameworks\native\services\inputflinger\reader\EventHub.cpp
EventHub::EventHub(void)
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
mOpeningDevices(nullptr), //等待open 的device 默认为空
mClosingDevices(nullptr), //等待close 的device 默认为空
mNeedToSendFinishedDeviceScan(false), //标记scan 完成
mNeedToReopenDevices(false), //标记是否需要reopen 所有input设备
mNeedToScanDevices(true), //标记是否scan 所有input 设备,默认是true
mPendingEventCount(0), //标记监听到的event 数量
mPendingEventIndex(0), //标记处理到的event index
mPendingINotify(false) { //标记是否收到dir notify,确认是否会触发readNotifyLocked
ensureProcessCanBlockSuspend();
mEpollFd = epoll_create1(EPOLL_CLOEXEC);//创建一个Epoll
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
mINotifyFd = inotify_init();//创建一个inotify,用以监听目录/dev/input 下是否有设备添加/删除
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
strerror(errno));
...
struct epoll_event eventItem = {};
eventItem.events = EPOLLIN | EPOLLWAKEUP;
eventItem.data.fd = mINotifyFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 将inotify 的id 添加到Epoll 监听
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
result = pipe(wakeFds); //创建pipe,用以强制唤醒getEvents,最终唤醒InputReader线程
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}
构造主要任务:
- mNeedToScanDevices 置true,用以第一次getEvent 时进行扫描;
- 创建Epoll 用以监听所有input 设备、inotity、wake pipe;
- 创建inotify 用以添加/dev/input 下设备的create/delete,对于inotify 详细可以参考https://www.cnblogs.com/jingzhishen/p/3738637.html
- 创建wake pipe,用以强制唤醒InputReader 线程,write 动作在EventHub::wake(),由InputReader 调用,read 动作在EventHub 的Epoll 中,用以强制结束getEvent;
2. getEvents 函数
getEvents 用以处理Epoll 所有的event,这里分段剖析。
2.1 确认是否reopen devcies
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
...
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
变量mNeedToReopenDevices 修改来自InputReader,InputReader thread 中会处理上层的请求,如果上层有发送CHANGE_MUST_REOPEN 请求,InputReader 进而会发送请求给EventHub。目前framework 代码中没有搜到上层发送 CHANGE_MUST_REOPEN 的地方。
-->
下面来详细分析 closeAllDevicesLocked()
void EventHub::closeAllDevicesLocked() {
mUnattachedVideoDevices.clear();
while (mDevices.size() > 0) {
closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
}
}
调用closeDeviceLocked 分别关闭每个设备:
void EventHub::closeDeviceLocked(Device* device) {
ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
device->identifier.name.c_str(), device->id, device->fd, device->classes);
if (device->id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
device->path.c_str(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
unregisterDeviceFromEpollLocked(device);
if (device->videoDevice) {
// This must be done after the video device is removed from epoll
mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
}
releaseControllerNumberLocked(device);
mDevices.removeItem(device->id);
device->close();
// Unlink for opening devices list if it is present.
Device* pred = nullptr;
bool found = false;
for (Device* entry = mOpeningDevices; entry != nullptr;) {
if (entry == device) {
found = true;
break;
}
pred = entry;
entry = entry->next;
}
if (found) {
// Unlink the device from the opening devices list then delete it.
// We don't need to tell the client that the device was closed because
// it does not even know it was opened in the first place.
ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
if (pred) {
pred->next = device->next;
} else {
mOpeningDevices = device->next;
}
delete device;
} else {
// Link into closing devices list.
// The device will be deleted later after we have informed the client.
device->next = mClosingDevices;
mClosingDevices = device;
}
}
该函数主要任务:
- 将该device 的fd 从Epoll 中移除;
- 将该device 从mDevices 中移除;
- 确认该device 是否正处于mOpeingDevices 中,如果在opening 中,将其移除;
- 将该device 添加到mClosingDevices 中,在getEvents 中会生成event 抛给InputReader;
2.2 确认是否发送DEVICE_REMOVED 消息
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
mClosingDevices = device->next;
event->when = now;
event->deviceId = (device->id == mBuiltInKeyboardId)
? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
: device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
代码比较简单,当有设备移除的时候,会调用closeDeviceLocked(),并将移除的device 添加到mClosingDevices 中, 这里会通过DEVICE_REMOVED 通知给InputReader。
注意变量mNeedToSendFinishedDeviceScan,用以最后消息FINISHED_DEVICE_SCAN 的结尾发送。
2.3 确认是否需要重新scan 设备
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
有两种情况会触发这里的scan:
- 第一次运行getEvents 时
- 要求reopen devices 时,详细查看2.1 节
-->
下面来看下scan devices 的过程:
status_t EventHub::scanDirLocked(const char* dirname) {
char devname[PATH_MAX];
char* filename;
DIR* dir;
struct dirent* de;
dir = opendir(dirname);
if (dir == nullptr) return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while ((de = readdir(dir))) {
if (de->d_name[0] == '.' &&
(de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}
- 通过opendir 确定目录名称,例如最开始是/dev/input,如果是目录,会在最后加上“/”,组成/dev/input/
- 排除文件 "." 和 ".." ,剩下来的文件通过openDeviceLocked() 打开处理
-->
继续来剖析openDeviceLocked() 的处理:
status_t EventHub::openDeviceLocked(const char* devicePath) {
char buffer[80];
int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
InputDeviceIdentifier identifier;
... //这里获取identifier 的所有属性,包括name/version/product/location/uniqueId/descriptor等等
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, devicePath, identifier);
...
// Load the configuration file for the device.
loadConfigurationLocked(device);
// Figure out the kinds of events the device reports.
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);
// 这里确认设备类型,存入device->classes中,如果device->classes 为0,delete 之前创建的device 实例
...
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device); //对于keyboard 或joystick 需要进行scan code与keycode 对应
}
...
if (registerDeviceForEpollLocked(device) != OK) {
delete device;
return -1;
}
configureFd(device);
...
addDeviceLocked(device);
return OK;
}
注意代码中的注释,这里处理过程:
- 打开设备节点,获取fd;
- 通过fd进行ioctl,确定设备的name、version、vendor、product、location、uniqueId 等信息,并存放到identifier 中,这里identifier 中存放着设备的所有信息,后期可以用来比对设备的信息,也可以根据identifier 进行Key map 的解析。
- 通过fd、identifier 创建Device 实例;
- 通过fd 进行ioctl 对device 进行一下初始化;
- 确认设备类型classes,例如INPUT_DEVICE_CLASS_KEYBOARD、INPUT_DEVICE_CLASS_CURSOR、INPUT_DEVICE_CLASS_TOUCH、INPUT_DEVICE_CLASS_JOYSTICK、INPUT_DEVICE_CLASS_VIBRATOR、INPUT_DEVICE_CLASS_MIC、INPUT_DEVICE_CLASS_DPAD等等;
- 通过registerDeviceForEpollLocked 注册设备的fd 到Epoll 进行event 监听;
- 将device 添加到mDevices 中,并更新mOpeningDevices 以便发送消息通知上层
注意,loadKeyMapLocked 需要进行scan code 与keycode 对应,后面单独一篇分析,详细请参考 《原理分析(三)》
2.4 确认是否发送DEVICE_ADDED 消息
while (mOpeningDevices != nullptr) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
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;
}
}
同消息DEVICE_REMOVED,当有设备新增时都会调用addDevcieLocked() 进行更新,并将消息DEVICE_ADDED 通知给InputReader。
2.5 确认是否发送消息 FINISHED_DEVICE_SCAN
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
在2.2节、2.3节、2.4节,设备发生变化时会有类似scan 的过程,都会将变量mNeedToSendFinishedDeviceScan 标记上,最后发送消息 FINISHED_DEVICE_SCAN 标记完成scan。
2.6 等待event 触发
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (pollResult < 0) {
...
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
进入epoll_wait 机制,等待epoll 的触发,触发后会将mPendingEventCount 和mPendingEventItems 带入下一次循环中进行解析。
2.7 处理event
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) { //epoll_wait 等到了event
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.fd == mINotifyFd) { //解析到了inotify event
if (eventItem.events & EPOLLIN) {
mPendingINotify = true; //标记需要read inotify
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.fd == mWakeReadPipeFd) { //解析到了wake pipe的event,即InputReader thread 需要唤醒
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true; //主要是置这个变量
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); //确认能读到pipe 内容,当时貌似不关心内容
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
Device* device = getDeviceByFdLocked(eventItem.data.fd); //通过fd 获取到device实例
if (!device) {
ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
continue; //如果没有这个设备或者被close了就忽略这个event
}
...
// This must be an input event
if (eventItem.events & EPOLLIN) { //如果是input device 的event
int32_t readSize =
read(device->fd, readBuffer, sizeof(struct input_event) * capacity); //读取input device 的具体信息
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { //如果设备异常,需要close掉
// 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 { //读取正确,将input device 的event抛回给InputReader 进行进一步解析
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
}
...
}
// readNotify() will modify the list of devices so this must be done after
// processing all other events to ensure that we read all remaining events
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) {
continue;
}
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
- 解析整个eventItem,需要全部的解析一遍,如果遇到特殊event,例如inotify 或者wake 则会进行标记,如果遇到input device 的event,则存放起来以便抛回给InputReader;
- 如果是出现了deviceChanged 时函数不会立即break,而是continue 进行DEVICE_ADDED / DEVICE_REMOVED 消息的记录,然后陪同input event 一起返回,即使有wake event同时存在的情况;
- 注意变量capacity,这个要求InputReader 在调用getEvents 需要确保记录event 的buffer 足够大;
至此,EventHub 分析基本完成,大致如下:
- 通过Epoll 对不同的fd 进行监听,fd 包括input device 、inotify fd、wake pipe fd;
- 在第一个getEvents 会对设备进行scan,后面根据inotify event 进行动态监听/dev/input 目录下设备的create/delete;
- 收到event 后inotify 的优先处理,会直接continue 循环进行DEVICE_ADDED / DEVICE_REMOVED 的消息记录;
- InputReader 在调用getEvents 时需要尽可能确保readBuffer 足够大;
|