Android getevent命令详细分析

在调试Android 的输入事件时,经常使用 "getevent -lrt" 命令,来确认驱动上报数据是否正常。从源码的角度来详细的分析一下getevent 这个程序。

首先用ls命令来看一下getevent

c 复制代码
lrwxr-xr-x 1 root shell 7 2023-11-20 10:08 system/bin/getevent -> toolbox

可以看出,getevent 链接的是toolbox,来看一下toolbox的main方法

c 复制代码
//system\core\toolbox\toolbox.c
int main(int argc, char** argv) {
    // Let's assume that none of this code handles broken pipes. At least ls,
    // ps, and top were broken (though I'd previously added this fix locally
    // to top). We exit rather than use SIG_IGN because tools like top will
    // just keep on writing to nowhere forever if we don't stop them.
    signal(SIGPIPE, SIGPIPE_handler);

    char* cmd = strrchr(argv[0], '/');
    char* name = cmd ? (cmd + 1) : argv[0];

    for (size_t i = 0; tools[i].name; i++) {
        if (!strcmp(tools[i].name, name)) {//name为getevent
            return tools[i].func(argc, argv);//1
        }
    }

    printf("%s: no such tool\n", argv[0]);
    return 127;
}

tools是一个数组

c 复制代码
//system\core\toolbox\toolbox.c
static struct {
    const char* name;
    int (*func)(int, char**);
} tools[] = {
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL
    { 0, 0 },
};

而在tools.h里面定义了 TOOL(getevent),将name替换为getevent,即注释1处调用getevent_main方法。接下来重点看一下getevent_main方法

c 复制代码
//system\core\toolbox\getevent.c
static struct pollfd *ufds;
static char **device_names;
static int nfds;

int getevent_main(int argc, char *argv[])
{
	//省略
	const char *device_path = "/dev/input";
	//省略
	nfds = 1;
    ufds = calloc(1, sizeof(ufds[0]));
    ufds[0].fd = inotify_init();//1
    ufds[0].events = POLLIN;
    if(device) {//getevent命令后面可以指定device,如getevent dev/input/event0
        //省略
    } else {
		if(!print_flags_set)
            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
        print_device = 1;
		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);//2
        res = scan_dir(device_path, print_flags); //3
	}
	
	//省略
}

注释1处进行inotify初始化,注释2处使用inotify监听/dev/input目录下是否有设备增加或者移除,注释3处扫描/dev/input目录。此时/dev/input目录下是已经存在的设备。来看一下scan_dir扫描操作

c 复制代码
//system\core\toolbox\getevent.c
static int scan_dir(const char *dirname, int print_flags)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        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);
        open_device(devname, print_flags);//1
    }
    closedir(dir);
    return 0;
}

注释1处,得到/dev/input目录下的设备名后,打开设备。注意该方法是处于循环中,会依次打开/dev/input目录下所有满足要求的设备。

c 复制代码
//system\core\toolbox\getevent.c
static int open_device(const char *device, int print_flags)
{
	//省略
	fd = open(device, O_RDONLY | O_CLOEXEC);//打开设备
	
	//调用ioctl获取信息
	
	/*加到ufds数组中*/
	ufds[nfds].fd = fd;
    ufds[nfds].events = POLLIN;
    device_names[nfds] = strdup(device);
    nfds++;

	return 0;
}

open_device方法主要功能是打开设备,然后将打开设备的fd添加进ufds数组中。

已有的设备扫描完成后,在getevent_main方法内,接着会进入一个循环

c 复制代码
//system\core\toolbox\getevent.c
int getevent_main(int argc, char *argv[])
{
	//省略
	while(1) {
        //int pollres =
        poll(ufds, nfds, -1);//1
        //printf("poll %d, returned %d\n", nfds, pollres);
        if(ufds[0].revents & POLLIN) {//2
            read_notify(device_path, ufds[0].fd, print_flags);
        }
        for(i = 1; i < nfds; i++) {
            if(ufds[i].revents) {
                if(ufds[i].revents & POLLIN) {//3
                    res = read(ufds[i].fd, &event, sizeof(event));
                    if(res < (int)sizeof(event)) {
                        fprintf(stderr, "could not get event\n");
                        return 1;
                    }
                    if(get_time) {
                        printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
                    }
                    if(print_device)
                        printf("%s: ", device_names[i]);
                    print_event(event.type, event.code, event.value, print_flags);//4
                    if(sync_rate && event.type == 0 && event.code == 0) {
                        int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
                        if(last_sync_time)
                            printf(" rate %lld", 1000000LL / (now - last_sync_time));
                        last_sync_time = now;
                    }
                    printf("%s", newline);
                    if(event_count && --event_count == 0)
                        return 0;
                }
            }
        }
    }
    return 0;
}

注释1处使用poll来监测ufds数组中的每项是否有数据产生,没数据时休眠,有数据产生就会被唤醒。

由于ufds[0]的fd放入的是inotify_init得到的fd,而inotify是用来监测/dev/input 下是否有设备增加或者删除。注释2处,如果ufds[0]的数据是POLLIN,则表示此时是有设备增加或者删除,调用read_notify方法处理。而注释3处表示是某个设备有输入事件了,则调用read读取事件并使用print_event打印出来。

c 复制代码
static int read_notify(const char *dirname, int nfd, int print_flags)
{
    int res;
    char devname[PATH_MAX];
    char *filename;
    char event_buf[512];
    int event_size;
    int event_pos = 0;
    struct inotify_event *event;

    res = read(nfd, event_buf, sizeof(event_buf));//读出数据
    
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';

    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) {//设备增加
                open_device(devname, print_flags);
            }
            else {
                close_device(devname, print_flags);
            }
        }
        event_size = sizeof(*event) + event->len;
        res -= event_size;
        event_pos += event_size;
    }
    return 0;
}

可以看出,在read_notify方法内,如果有设备增加,也是调用open_device来进行处理。open_device在前面分析过,主要是打开设备并将fd添加到ufds数组中。

总结

getevent 使用 inotify和poll机制来监测有无输入设备的增加和删除,以及哪个设备有数据产生。具体的工作流程如下图

相关推荐
还鮟4 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡5 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi005 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil7 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你7 小时前
Android View的绘制原理详解
android
移动开发者1号10 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号10 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best15 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk15 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭20 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin