Android getevent命令详细分析

news2024/11/25 23:40:27

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

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

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

//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是一个数组

//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方法

//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扫描操作

//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目录下所有满足要求的设备。

//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方法内,接着会进入一个循环

//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打印出来。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1648818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频号小店是普通人的机会吗?看完你就明白了!

大家好&#xff0c;我是电商小V 视频号小店是普通人的机会吗&#xff1f;我可以很确定的说&#xff1a;视频号小店就是普通人的机会&#xff0c;并且是很大的机会&#xff0c; 首先就是视频号小店这个项目还没有自然流量的入口&#xff0c;是一个还没有完全开放私域电商的平台&…

python+flask+ldap3搭建简易版IDaaS系统(前端站点)

Python工具开源专栏 Py0006 pythonflaskldap3搭建简易版IDaaS系统&#xff08;前端站点&#xff09; Python工具开源专栏前言目录结构前端网站的部分演示首页查询数据数据同步数据关联查询系统日志 完整代码已在GitHub上开源 前言 pythonflaskldap3搭建简易版IDaaS系统的前端站…

XSS漏洞---XSS-labs通关教程

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 Level-1 过滤源码&#xff1a;无 pyload&#xff1a; name<script>alert(1)</script> Level-2 过滤源码&#xff1a;利用转译函数将特殊字符转译为实体字符 $str $_GET["…

RNN 及其变体的数学理解

目录 一、介绍&#xff1a; 二、什么是 RNN 以及它与前馈神经网络有何不同&#xff1a; 三、RNN 背后的数学 3.1 RNN的数学方程 3.2 参数和梯度 3.2.1 损耗相对于V的梯度 3.2.2 损失相对于W的梯度 四、RNN 变体&#xff08;LSTM 和 GRU&#xff09; 4.1 LSTM&#xff08;长短期…

代码随想录算法训练营第36期DAY19

DAY19 104二叉树的最大深度 根节点的高度就是最大深度。 非递归法&#xff1a; /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) …

Ansible自动运维工具之playbook

一.inventory主机清单 1.定义 Inventory支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内。 2.变量 &#xff08;1&#xff09;主机变量 [webservers] 192.168.10.14 ansible_port22 ansible_userroot ans…

央国企加速新质生产力形成和发展,HR数字化工具如何推动创新内核构建?

自今年两会以来&#xff0c;“新质生产力”一词获得了广泛的关注。众多专家学者对其重要性、定义及作用进行了热烈且深入的讨论&#xff0c;一致强调了新质生产力的核心地位。对于那些致力于转型为现代化国有企业的国资中央企业而言&#xff0c;培育新质生产力无疑成为了当前及…

JVM笔记-常用命令

1、jstat jstat是一个极强的监视JVM的工具&#xff0c;可以用来监视JVM的各种堆和非堆的大小以及内存使用量。 Usage: jstat -help|-optionsjstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]jstat的常用用法如图所示&#xff…

Java Swing手搓童年坦克大战游戏(III)

坦克大战豪华山寨版二期工程 计划&#xff1a;实现【道具功能】【分数统计、排行榜】【多种类型敌军坦克派遣】【自建地图】【游戏存档读档】【联网实现双人配合】等&#xff0c;修复一些严重的bug。由于功能比较多&#xff0c;目测会分多篇文章记录…… 前言 通过对原游戏的…

C语言 函数概述

好 接下来 我们来讲函数 构建C程序的最佳方式 就是模块化程序设计 C语言中 最基本的程序模块被称为 函数 所以 这个知识点的重要性不言而喻 这里 我们讲个故事 诸葛亮六出祁山时 为了逼司马懿出战 派人送给力司马懿一件女人衣服 司马懿只是为使者 诸葛亮的饮食起居 使者感叹…

光电探测器性能指标测试

光电探测器的三个核心指标&#xff1a; 带宽&#xff0c;转换增益&#xff0c;噪声(信噪比&#xff0c;NEP&#xff0c;噪声密度) 测试环境&#xff1a;可调谐激光器&#xff08;CW LASER&#xff09;&#xff0c;强度调制器(AM)&#xff0c;信号发生器(AWG)&#xff0c;可调衰…

stm32f103zet6_DAC_2_输出电压

实现效果 DAC输出的电压 同过电压表测量电压 1.DAC配置的步骤 初始化DAC时钟。配置DAC的GPIO端口。设置DAC的工作模式&#xff08;例如&#xff0c;是否使用触发功能&#xff0c;是否启用DAC中断等&#xff09;。启动DAC。 2常用的函数 函数 HAL_DAC_Start() - 开启指定…

5月6(信息差)

&#x1f30d;一次预测多个token&#xff0c;Meta新模型推理加速3倍&#xff0c;编程任务提高17% https://hub.baai.ac.cn/view/36857 &#x1f384; LeetCode 周赛超越 80% 人类选手&#xff0c;推理性能超 Llama3-70B。 ✨ 我国量子计算机实现“四算合一” 实现通算、…

CAPL如何实现TLS握手认证

CAPL有专门的章节介绍如何实现TLS握手认证的函数: CAPL调用哪些函数实现TLS握手认证,需要了解TLS在整个通信过程的哪个阶段。 首先TCP需要建立连接,这是TLS握手的前提。当TLS握手认证完成后,可以传输数据。 所以TLS握手开始前需要确保TCP建立连接,TCP传输数据前需要确保…

5.6代码

1.最大公约数 这个题最重要的是要找到一个区间是1&#xff0c;找到之后就可以直接加次数就可以了 #include <bits/stdc.h>using namespace std;main() {long long n,i,j,a0,b,ans99999;cin>>n;long long s[n],dp[n][n];for(i0;i<n;i){cin>>s[i];if(s[i]1…

五一 作业

#include <iostream>using namespace std; class Num { private:int a; public:Num() {}Num(int a):a(a){}//设置a的值void set(int a){this->aa;}//1-a的和void Sum(){if(a<1){cout<<"a<1"<<endl;return;}int sum0;for(int i1;i<a;i)…

荧光纳米纤维膜的优点

荧光纳米纤维膜是一种具有荧光特性的纳米纤维材料&#xff0c;它结合了纳米纤维的良好性能和荧光物质的发光特性&#xff0c;在多个领域具有潜在的应用价值。 荧光纳米纤维膜可以通过不同的制备方法得到&#xff0c;其中一种常见的方法是利用荧光物质对纳米纤维进行修饰或掺杂。…

如何从Windows 10电脑远程登录Ubuntu系统

要从Windows 10电脑远程登录Ubuntu系统&#xff0c;您可以使用以下步骤&#xff1a; 在Ubuntu上安装xRDP: 首先&#xff0c;在Ubuntu电脑上打开终端&#xff0c;然后输入以下命令来安装xRDP服务&#xff1a; sudo apt update sudo apt install xrdpxRDP是一个开源的远程桌面协议…

【docker 】 push 镜像提示:denied: requested access to the resource is denied

往 Docker Registry &#xff08;私服&#xff09;push 镜像提示&#xff1a;denied: requested access to the resource is denied 镜像push 语法&#xff1a;docker push <registry-host>:<registry-port>/<repository>:<tag> docker push 192.16…

QT开发(四) 制作一个JSON检查小工具

1、JSON概念 1.1 定义 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它易于人类阅读和编写&#xff0c;同时也易于机器解析和生成。JSON基于JavaScript语言的子集&#xff0c;但是独立于编程语言&#xff0c;因此可以被多种…