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机制来监测有无输入设备的增加和删除,以及哪个设备有数据产生。具体的工作流程如下图

相关推荐
Gary Studio11 小时前
Android AIDL HAL工程结构示例
android
y = xⁿ11 小时前
MySQL八股知识合集
android·mysql·adb
andr_gale12 小时前
04_rc文件语法规则
android·framework·aosp
祖国的好青年13 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴13 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭14 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首14 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose
zhangphil15 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin
神探小白牙15 小时前
echarts,3d堆叠图
android·3d·echarts