Android input epoll/inotify机制

颜森
2023-12-01

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;
}

 类似资料: