Android Input系统9 INotify与Epoll机制
熟悉或者了解过input机制的,知道在native层会有EventHub去接受底层input上报的数据,下面的例子主要从EventHub中拆解出来的,能简单实现(1).检测是否有新的input设备插入<在dev/input中新增文件测试>,(2).检测是否有新的input_event上报,
inotify能监听内核文件系统变化的机制,可以监听文件/目录的增删,INotify 有个问题就是需要主动调用 read 函数去读取事件,Input 系统需要的是 INotify 监听的目标路径发生变化之后来能通知自己,这就需要结合另一个机制 Epoll 来实现,Epoll 是一种 I/O 多路复用技术,主要作用就是去监听 Linux 下的 fd,当这些 fd 发生事件之后,会通过回调来通知 Epoll。
最终EventHub的实现流程也是如此,首先就是将kernel驱动中回去注册input节点对应eventx,再通过inotify的机制监听对应路径dev/input将其都保存好event->name,最后通过open 保存好的eventx的节点获取fd来读取事件进行处理。
不仅input也用到了Epoll机制,电池电量上报的过程也是如此,Android中healthd就是一直读取kernel中的节点信息sys/class/power_supply,其实从流程上来最终读取的流向battery->bms->battery,这里battery则对应的就是驱动的电池驱动了,之前又遇到或电池电量快速跌落,就是电池驱动中的读取时发生错误,可通过判断从cache中获取来避免,这里还涉及到kernle中的uevent的上报操作(kobject_uevent/add_uevent_var/netlink)等等,知识点非常多,等后续有空再实例操作研究一下具体的机制
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <log/log.h>
#include "EventHub.h"
#define bufferSize 512
#define EPOLL_MAX_EVENTS 16
static const char* DEVICE_PATH = "/dev/input";
/*
* inotify机制: 监视某个目录,当状态发生变化,比如文件创建,删除,重命名等
* 则发送对应的文件时间inotify_event
* inotify->wd = 触发事件的文件所对应的监视描述符
* inotify->mask= IN_CREATE(文件或目录被创建) IN_DELETE(文件或目录被删除)等
* inotify->len = sizeof(struct inotify_event) + event->len
*
* design two part:
* (1) detect the /dev/input dir/file create/delete
* (2) detect the /dev/input/event3 input_event
*/
int main(){
int result;
int mEpollFd;
int mINotifyFd;
int mInputWd;
int timeoutMillis = -1;
size_t mPendingEventCount = 0;
size_t mPendingEventIndex = 0;
struct epoll_event eventItem1 = {};
struct epoll_event eventItem2 = {};
struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
size_t capacity = bufferSize;
struct input_event readBuffer[bufferSize];
#if 1
int res;
int event_size;
int event_pos = 0;
char event_buf[512];
struct inotify_event *event;
#endif
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
mINotifyFd = inotify_init();
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
//1.dev/input
eventItem1.events = EPOLLIN | EPOLLWAKEUP;
eventItem1.data.fd = mINotifyFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem1);
if(result != 0)
ALOGE("jerry epoll_ctl notifyFd dev/input error");
//2.dev/input/event2
int fd = open("/dev/input/event2", O_RDWR | O_CLOEXEC | O_NONBLOCK);
if(fd < 0)
ALOGD("jerry failed to open the dev/input/event2");
eventItem2.events = EPOLLIN | EPOLLWAKEUP;
eventItem2.data.fd = fd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem2);
if(result != 0)
return 0;
while(true){
ALOGD("jerry epoll wait");
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPendingEventCount = size_t(pollResult);
ALOGD("jerry epoll happend count = %zu", mPendingEventCount);
#if 1
//1.监听目录下文件状态变化 EventHub::readNotifyLocked
res = read(mINotifyFd, event_buf, sizeof(event_buf));
ALOGD("jerry read the dev/input change buffer size = %d", res);
while (res >= (int)sizeof(*event)) {
event = (struct inotify_event*)(event_buf + event_pos);
ALOGD("jerry get the inotify_event lens = %d", event->len);
if (event->len) {
if (event->wd == mInputWd) {
std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
ALOGD("jerry inotify file create name %s", filename.c_str());
} else {
ALOGI("jerry Removing device '%s' due to inotify event\n", filename.c_str());
}
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
#endif
//2.监听目录中eventx上报inout_event EventHub::getEvents + registerFdForEpoll
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
ALOGD("jerry get the event type = %d",eventItem.events);
if (eventItem.events & EPOLLIN) {
ALOGD("jerry input event happend");
int32_t readSize = read(fd, readBuffer, sizeof(struct input_event) * capacity);
if ((readSize % sizeof(struct input_event)) != 0 || readSize <= 0) {
ALOGE("jerry get event (wrong size: %d) input_event size = %ld", readSize, sizeof(struct input_event));
}else{
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
ALOGD("jerry event type = %d code = %d value = %d",iev.type,iev.code,iev.value);
capacity -= 1;
}
}
}
}
mPendingEventIndex = 0;
}
close(mINotifyFd);
close(mEpollFd);
return 0;
}