AOSP15 Input专题getevent深入分析

在 AOSP 15 中,getevent 是输入系统(Input System)调试的"第一现场"。它能够跳过复杂的 Android Framework 逻辑,直接读取 Linux 内核上报的原始事件(Raw Events)。

以下是对 AOSP 15 中 getevent 的源码深度解析、工作流程及常用命令指南。

一、 源码与编译逻辑分析

1. getevent是什么

在 AOSP 15 源码树中,getevent 的位置非常核心,它是 toolbox 工具集的一部分:

  • 核心源码: system/core/toolbox/getevent.c
c++ 复制代码
int getevent_main(int argc, char *argv[])
{
    int c;
    int i;
    int res;
    int get_time = 0;
    int print_device = 0;
    char *newline = "\n";
    uint16_t get_switch = 0;
    struct input_event event;
    int print_flags = 0;
    int print_flags_set = 0;
    int dont_block = -1;
    int event_count = 0;
    int sync_rate = 0;
    int64_t last_sync_time = 0;
    const char *device = NULL;
    const char *device_path = "/dev/input";

    /* disable buffering on stdout */
    setbuf(stdout, NULL);

    opterr = 0;
    do {
        // 解析命令
        c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
        if (c == EOF)
            break;
        switch (c) {
        case 't':
            get_time = 1;
            break;
        case 'n':
            newline = "";
            break;
        case 's':
            get_switch = strtoul(optarg, NULL, 0);
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'S':
            get_switch = ~0;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'v':
            if(optarg)
                print_flags |= strtoul(optarg, NULL, 0);
            else
                print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
            print_flags_set = 1;
            break;
        case 'd':
            print_flags |= PRINT_HID_DESCRIPTOR;
            break;
        case 'p':
            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE
                    | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;
            print_flags_set = 1;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'i':
            print_flags |= PRINT_ALL_INFO;
            print_flags_set = 1;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'l':
            print_flags |= PRINT_LABELS;
            break;
        case 'q':
            print_flags_set = 1;
            break;
        case 'c':
            event_count = atoi(optarg);
            dont_block = 0;
            break;
        case 'r':
            sync_rate = 1;
            break;
        case '?':
            fprintf(stderr, "%s: invalid option -%c\n",
                argv[0], optopt);
        case 'h':
            usage(argv[0]);
            exit(1);
        }
    } while (1);
    if(dont_block == -1)
        dont_block = 0;

    if (optind + 1 == argc) {
        device = argv[optind];
        optind++;
    }
    if (optind != argc) {
        usage(argv[0]);
        exit(1);
    }
    //初始化 poll 结构
    nfds = 1;
    ufds = calloc(1, sizeof(ufds[0]));
    // 监听 /dev/input 目录变化(插拔设备)
    ufds[0].fd = inotify_init();
    ufds[0].events = POLLIN;
    if(device) {
        if(!print_flags_set)
            print_flags |= PRINT_DEVICE_ERRORS;
        // 打开设备
        res = open_device(device, print_flags);
        // 打开失败
        if(res < 0) {
            return 1;
        }
    } else {
        if(!print_flags_set)
            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
        print_device = 1;
        // 加入第一个目录到fd
		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
        if(res < 0) {
            fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
            return 1;
        }
        // 扫描监听加入fd
        res = scan_dir(device_path, print_flags);
        if(res < 0) {
            fprintf(stderr, "scan dir failed for %s\n", device_path);
            return 1;
        }
    }
    // 设备状态
    if(get_switch) {
        for(i = 1; i < nfds; i++) {
            uint16_t sw;
            res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
            if(res < 0) {
                fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
                return 1;
            }
            sw &= get_switch;
            printf("%04x%s", sw, newline);
        }
    }

    if(dont_block)
        return 0;

    while(1) {
        //int pollres =
        poll(ufds, nfds, -1);
        // 监听 /dev/input 目录变化(插拔设备)
        if(ufds[0].revents & POLLIN) {
            read_notify(device_path, ufds[0].fd, print_flags);
        }
        // ufds[1..n] = input 设备 fd
        for(i = 1; i < nfds; i++) {
            if(ufds[i].revents) {
                if(ufds[i].revents & POLLIN) {
                    res = read(ufds[i].fd, &event, sizeof(event));
                    if(res < (int)sizeof(event)) {
                        fprintf(stderr, "could not get evdev event, %s\n", strerror(errno));
                        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);
                    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;
}
  • 二进制路径: /system/bin/getevent
  • 依赖头文件: bionic/libc/kernel/uapi/linux/input.h (定义了输入协议的数据结构,如 input_event)
c++ 复制代码
// linux/input.h
struct input_event {
    struct timeval time; // 时间戳
    __u16 type;          // 事件类型 (如 EV_KEY, EV_ABS)
    __u16 code;          // 事件代码 (如 BTN_TOUCH, ABS_MT_POSITION_X)
    __s32 value;         // 事件值 (如 1 为按下, 0 为抬起, 或坐标值)
};

2、核心代码逻辑分析

getevent 的实现本质上是一个 多路复用(I/O Multiplexing) 的监听器。

Step 1: 扫描设备节点

程序启动后,会遍历 /dev/input/ 目录。通过 scandir 获取所有 eventX 节点。

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);
        // 加入nfds监听
        open_device(devname, print_flags);
    }
    closedir(dir);
    return 0;
}

Step 2: 轮询监听 (Poll)

c++ 复制代码
int getevent_main(int argc, char *argv[])
{
   // ...

    while(1) {
        // poll 轮询监听,阻塞等待,直到有硬件输入
        poll(ufds, nfds, -1);
        if(ufds[0].revents & POLLIN) {
            read_notify(device_path, ufds[0].fd, print_flags);
        }
        for(i = 1; i < nfds; i++) {
            if(ufds[i].revents) {
                if(ufds[i].revents & POLLIN) {
                    
                    res = read(ufds[i].fd, &event, sizeof(event));

                    // ...

                    // 格式化输出到控制台
                     print_event(event.type, event.code, event.value, print_flags);

                     // ...
                }
            }
        }
    }
    return 0;
}

Step 3: 数据解析

它读取的是内核定义的 struct input_event。如果是带有 -l 参数,它会查询内置的字符串表,将 0003 0035 这种十六进制转换为人类可读的 EV_ABS ABS_MT_POSITION_X。

3. 常用命令

在终端(adb shell)中,getevent 提供了丰富的参数来过滤和展示数据,用比较多的命令 -lrt。

参数 说明 使用场景
-p Print capabilities 查看设备支持哪些事件(如是否支持多点触控、压力感应)。
-i Get ID info 查看设备的 Vendor ID、Product ID,确认是否匹配了正确的 .idc 文件。
-l Label output 将十六进制代码转换为文字(如 EV_KEY),最常用。
-t Timestamp 显示精确到微秒的时间戳,用于分析报点延迟。
-r Rate 显示实时报点频率(Hz),查看屏幕触控采样率。
-s Switch status 查看开关状态(如盖合传感器、耳机孔插拔状态)。

二、协议指令分析和实战

1、类型

1.1 EV_SYN

同步事件完,在事件开始或完成会有,对应的code:

  • 0004:代表一个事件开始(不必要)
  • 0005:代表一个事件开始(不必要)
  • SYN_REPORT:代表一个事件的结束 (必要)

1.2 EV_ABS

事件的一种绝对坐标类型,对应code:

  • ABS_MT_SLOT: 本质代表者不同手指,它的value代表手指id
  • ABS_MT_TRACKING_ID: 类型B特有的,实际上,每个slot会和一个ID相对应,一个非负数的表示一次接触,-1表示这是一个无用的slot(或者理解为一次接触的结束) 。无论在接触的类型相对应的slot发生了改变,驱动都应该通过改变这个值来使这个slot失效。并且下一次触摸的ID值会是这次的值加1。
  • ABS_MT_POSITION_X,ABS_MT_POSITION_Y: 相对于屏幕中心的x,y坐标。
  • ABS_MT_TOUCH_MAJOR: 接触部分的长轴长度。相当于椭圆的长轴。
  • ABS_MT_TOUCH_MINOR: 接触部分的短轴长度。相当于椭圆的短轴。
  • ABS_MT_PRESSURE: 代表按下压力,有的设备不一定有

1.3 EV_KEY

事件的一种类型。表示是按键(不仅仅指的物理按键也包括TOUCH)事件,对应code:

  • BTN_TOUCH: 触碰按键。其值是DOWN或者UP。
  • BTN_TOOL_FINGER: 按键的是finger,并且其值也是DOWN或者UP

2、案例分析, 因为模拟器问题,实践设备没办法

bash 复制代码
add device 1: /dev/input/event3
  name:     "ilitek_ts"
add device 2: /dev/input/event2
  name:     "Newland Auto-ID NLS IOTC PRDs HID KBW"
add device 3: /dev/input/event1
  name:     "rk29-keypad"
add device 4: /dev/input/event0
  name:     "rk_headset"
[    1021.576809] /dev/input/event3: EV_KEY       BTN_TOUCH            DOWN                
[    1021.576809] /dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000007      第一个手指对应的TRACKING_ID      
[    1021.576809] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    00001416      按下X轴坐标
[    1021.576809] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    000009f3      按下Y轴坐标      
[    1021.576809] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080      按下的椭圆长轴        
[    1021.576809] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001      按下的椭圆长轴       
[    1021.576809] /dev/input/event3: EV_SYN       SYN_REPORT           00000000      --同步尾(不省略)       
.....     
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000001      代表第二手指出来了      
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000008      第二个手指对应的TRACKING_ID      
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    000010a1      第二手指坐标      
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    0000164c      第二手指坐标       
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.601178] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.601178] /dev/input/event3: EV_SYN       SYN_REPORT           00000000            
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000000      第一手指    
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000001            
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.611940] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.611940] /dev/input/event3: EV_SYN       SYN_REPORT           00000000            
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000000      第一手指       
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    0000141f      第一手指坐标      
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    00000a08      第一手指坐标     
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000001      第二手指      
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080      第二手指坐标      
[    1021.622700] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001      第二手指坐标      
[    1021.622700] /dev/input/event3: EV_SYN       SYN_REPORT           00000000      
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000000            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    0000155e            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    00000d33            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000001            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    000010f7            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    00001713            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.668726] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.668726] /dev/input/event3: EV_SYN       SYN_REPORT           00000000           
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000000            
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_POSITION_X    0000160a            
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    00000ef2            
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000080            
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_TOUCH_MAJOR   00000001            
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000001          
[    1021.679451] /dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   ffffffff   第二个手指消失抬起           
[    1021.679451] /dev/input/event3: EV_SYN       SYN_REPORT           00000000             
[    1021.690232] /dev/input/event3: EV_ABS       ABS_MT_SLOT          00000000            
[    1021.690232] /dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   ffffffff   第一个手指消失抬起         
[    1021.690232] /dev/input/event3: EV_KEY       BTN_TOUCH            UP                  
[    1021.690232] /dev/input/event3: EV_SYN       SYN_REPORT           00000000            
相关推荐
莞凰8 小时前
昇腾CANN的“灵脉根基“:Runtime仓库探秘
android·人工智能·transformer
NiceCloud喜云9 小时前
Claude Files API 深入:从上传、复用到配额管理的工程化指南
android·java·数据库·人工智能·python·json·飞书
ujainu9 小时前
CANN pto-isa:虚拟指令集如何连接编译与执行
android·ascend
赏金术士10 小时前
第六章:UI组件与Material3主题
android·ui·kotlin·compose
TechMerger11 小时前
Android 17 重磅重构!服役 20 年的 MessageQueue 迎来无锁改造,卡顿大幅优化!
android·性能优化
yuhuofei202114 小时前
【Python入门】Python中字符串相关拓展
android·java·python
dalancon14 小时前
Android Input Spy Window
android
dalancon16 小时前
InputDispatcher派发事件,查找目标窗口
android
我命由我1234516 小时前
Android Framework P3 - MediaServer 进程、认识 ServiceManager 进程
android·c语言·开发语言·c++·visualstudio·visual studio·android runtime
天才少年曾牛17 小时前
Android14 新增系统服务后,应用调用出现 “hidden api” 警告的原因与解决方案
android·frameworks