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            
相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui