android Input专题-getevent深入分析
Android手机大厂Framework系统-Input系统专题实战课
https://ke.qq.com/course/4963459
[入门课,实战课,跨进程专题
ps需要学习深入framework课程和课程优惠
新课程优惠获取请加入qq群:422901085
hi,本节课我们来讲解一下经常做触摸相关开发同学都必须要掌握的一个命令:
getevent
下面将通过2个部分来分别讲解
1、具体使用方法
gemini:/ $ getevent -hUsage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device] -t: show time stamps -n: don't print newlines -s: print switch states for given bits -S: print all switch states -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64) -d: show HID descriptor, if available -p: show possible events (errs, dev, name, pos. events) -i: show all device info and possible events -l: label event types and names in plain text -q: quiet (clear verbosity mask) -c: print given number of events then exit -r: print rate events are received
这里我们来简单看一下这个命令使用方式,上面列出了很多参数对应的解释,这里就不一一讲解了,我们就几个主要的
-t: show time stamps --显示事件的发生时间
-l: label event types and names in plain text —这里表示要把event事件类型名字打印出来
-r: print rate events are received --显示一下接受事件速率
一般调试使用命令: getevent -lrt
1|gemini:/ $ getevent -lrt add device 1: /dev/input/event6 name: "uinput-fpc"add device 2: /dev/input/event5 name: "msm8996-tasha-mtp-snd-card Button Jack"add device 3: /dev/input/event4 name: "msm8996-tasha-mtp-snd-card Headset Jack"add device 4: /dev/input/event2 name: "gpio-keys"add device 5: /dev/input/event0 name: "qpnp_pon"could not get driver version for /dev/input/mice, Not a typewriteradd device 6: /dev/input/event3 name: "pwm-ir"could not get driver version for /dev/input/mouse0, Not a typewriteradd device 7: /dev/input/event1 name: "synaptics_dsx"[ 160896.797487] /dev/input/event0: EV_KEYKEY_POWER DOWN [ 160896.797487] /dev/input/event0: EV_SYNSYN_REPORT 00000000 [ 160896.915581] /dev/input/event0: EV_KEYKEY_POWER UP [ 160896.915581] /dev/input/event0: EV_SYNSYN_REPORT 00000000 rate 8[ 160898.678739] /dev/input/event1: EV_SYN0004 00027482 [ 160898.678739] /dev/input/event1: EV_SYN0005 284ca8a3 [ 160898.678739] /dev/input/event1: EV_ABSABS_MT_TRACKING_ID 00000052 [ 160898.678739] /dev/input/event1: EV_KEYBTN_TOUCH DOWN [ 160898.678739] /dev/input/event1: EV_KEYBTN_TOOL_FINGER DOWN [ 160898.678739] /dev/input/event1: EV_ABSABS_MT_POSITION_X 00000228 [ 160898.678739] /dev/input/event1: EV_ABSABS_MT_POSITION_Y 000006f5 [ 160898.678739] /dev/input/event1: EV_ABSABS_MT_TOUCH_MAJOR 00000003
这里就会打印出对应的格式进行介绍
[ 160898.678739] /dev/input/event1: EV_ABS ABS_MT_TOUCH_MAJOR 00000003
时间 具体节点文件名 事件类型 事件code 事件value
从以上打印就可以看出我们此时手机产生了哪些事件
2、getevent源码分析
system/core/toolbox/getevent.c
//省略部分static void print_event(int type, int code, int value, int print_flags){ const char *type_label, *code_label, *value_label; if (print_flags & PRINT_LABELS) { type_label = get_label(ev_labels, type); code_label = NULL; value_label = NULL; switch(type) { case EV_SYN: code_label = get_label(syn_labels, code); break; case EV_KEY: code_label = get_label(key_labels, code); value_label = get_label(key_value_labels, value); break; case EV_REL: code_label = get_label(rel_labels, code); break; case EV_ABS: code_label = get_label(abs_labels, code); switch(code) { case ABS_MT_TOOL_TYPE: value_label = get_label(mt_tool_labels, value); } break; case EV_MSC: code_label = get_label(msc_labels, code); break; case EV_LED: code_label = get_label(led_labels, code); break; case EV_SND: code_label = get_label(snd_labels, code); break; case EV_SW: code_label = get_label(sw_labels, code); break; case EV_REP: code_label = get_label(rep_labels, code); break; case EV_FF: code_label = get_label(ff_labels, code); break; case EV_FF_STATUS: code_label = get_label(ff_status_labels, code); break; } if (type_label) printf("%-12.12s", type_label); else printf("%04x ", type); if (code_label) printf(" %-20.20s", code_label); else printf(" %04x ", code); if (value_label) printf(" %-20.20s", value_label); else printf(" %08x ", value); } else { printf("%04x %04x %08x", type, code, value); }}//省略部分static int open_device(const char *device, int print_flags){ int version; int fd; int clkid = CLOCK_MONOTONIC; struct pollfd *new_ufds; char **new_device_names; char name[80]; char location[80]; char idstr[80]; struct input_id id; fd = open(device, O_RDONLY | O_CLOEXEC);//打开这个节点 if(fd < 0) { if(print_flags & PRINT_DEVICE_ERRORS) fprintf(stderr, "could not open %s, %s\n", device, strerror(errno)); return -1; } if(ioctl(fd, EVIOCGVERSION, &version)) { if(print_flags & PRINT_DEVICE_ERRORS) fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno)); return -1; } if(ioctl(fd, EVIOCGID, &id)) { if(print_flags & PRINT_DEVICE_ERRORS) fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno)); return -1; } name[sizeof(name) - 1] = '\0'; location[sizeof(location) - 1] = '\0'; idstr[sizeof(idstr) - 1] = '\0'; if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno)); name[0] = '\0'; } if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno)); location[0] = '\0'; } if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno)); idstr[0] = '\0'; } if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) { fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno)); // a non-fatal error } new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); if(new_ufds == NULL) { fprintf(stderr, "out of memory\n"); return -1; } ufds = new_ufds; new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1)); if(new_device_names == NULL) { fprintf(stderr, "out of memory\n"); return -1; } device_names = new_device_names; if(print_flags & PRINT_DEVICE) printf("add device %d: %s\n", nfds, device); if(print_flags & PRINT_DEVICE_INFO) printf(" bus: %04x\n" " vendor %04x\n" " product %04x\n" " version %04x\n", id.bustype, id.vendor, id.product, id.version); if(print_flags & PRINT_DEVICE_NAME) printf(" name: \"%s\"\n", name); if(print_flags & PRINT_DEVICE_INFO) printf(" location: \"%s\"\n" " id:\"%s\"\n", location, idstr); if(print_flags & PRINT_VERSION) printf(" version: %d.%d.%d\n", version >> 16, (version >> 8) & 0xff, version & 0xff); if(print_flags & PRINT_POSSIBLE_EVENTS) { print_possible_events(fd, print_flags); } if(print_flags & PRINT_INPUT_PROPS) { print_input_props(fd); } if(print_flags & PRINT_HID_DESCRIPTOR) { print_hid_descriptor(id.bustype, id.vendor, id.product); } ufds[nfds].fd = fd;//非常关键点,这里赋值给了ufds,后面会把ufds统一加入poll ufds[nfds].events = POLLIN;//感兴趣的事件只有POLLIN,即有数据可以读取了 device_names[nfds] = strdup(device); nfds++; return 0;}int close_device(const char *device, int print_flags){ int i; for(i = 1; i < nfds; i++) { if(strcmp(device_names[i], device) == 0) { int count = nfds - i - 1; if(print_flags & PRINT_DEVICE) printf("remove device %d: %s\n", i, device); free(device_names[i]); memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count); memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); nfds--; return 0; } } if(print_flags & PRINT_DEVICE_ERRORS) fprintf(stderr, "remote device: %s not found\n", device); return -1;}//读取/dev/input路径下面的节点内容变化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)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; fprintf(stderr, "could not get event, %s\n", strerror(errno)); return 1; } //printf("got %d bytes of event information\n", res); 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;}//读取扫描路径下的节点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); } closedir(dir); return 0;}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); } nfds = 1; ufds = calloc(1, sizeof(ufds[0])); ufds[0].fd = inotify_init();//初始化inotify主要负责监听文件夹内容变化 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; //添加inotify观察路径为device_pathres = 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; } res = scan_dir(device_path, print_flags);//扫描遍历所有节点,且打开节点 if(res < 0) { fprintf(stderr, "scan dir failed for %s\n", device_path); return 1; } } //省略部分 while(1) { //int pollres = poll(ufds, nfds, -1); //最为核心的poll类似以前说过epoll,但是效率比epoll低 //printf("poll %d, returned %d\n", nfds, pollres); if(ufds[0].revents & POLLIN) {//第1个fd其实是文件夹里面内容变化 read_notify(device_path, ufds[0].fd, print_flags); } for(i = 1; i < nfds; i++) { if(ufds[i].revents) { if(ufds[i].revents & POLLIN) {//非第1个fd其实具体节点内容有变化 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);//打印具体event内容 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;}
从代码可以看出其实这个程序整体逻辑是非常非常清晰,主要就分为2个大部分:
1、监听/dev/input目录中里面内容的变化,即该目录下是否有增加和减少节点
2、监听/dev/input下面具体每个节点中的内容变化,如果有内容变化则读取内容打印出读取内容
ps:这里补充一下getevent_main怎么被调用执行的
其实getevent只是toolbox这个二进制可执行程序的一个软连接,所以getevent命令其实就是执行toolbox这个二进制文件,所以来看toolbox.c
#define TOOL(name) int name##_main(int, char**);//这里会拼接名字#include "tools.h"//这里内容其实就是TOOL(getevent) TOOL(getprop) TOOL(toolbox)#undef TOOLint main(int argc, char** argv) {signal(SIGPIPE, SIGPIPE_handler); char* cmd = strrchr(argv[0], '/'); char* name = cmd ? (cmd + 1) : argv[0];//获取名字name为getevent for (size_t i = 0; tools[i].name; i++) { if (!strcmp(tools[i].name, name)) { return tools[i].func(argc, argv);//这句是关键。这里就会调用到getevent_main方法 } } printf("%s: no such tool\n", argv[0]); return 127;}