Android Input 子系统初探

标题Android Input 子系统初探

在这里插入图片描述
Android系统是基于Linux内核构建的,作为操作系统的核心,内核负责管理和控制硬件设备的操作。它不仅驱动硬件,还为操作系统提供必要的核心功能。Android支持多种输入设备,如按键、触摸屏和手柄等。面对这些多样化的输入设备,内核通过一种抽象化的方式统一处理这些设备的核心流程,同时允许在具体实现上保留差异化的处理(通过不同类型的回调函数来实现)。这正是Input子系统的作用,主要包括以下几方面:

  • 规范化定义Input设备及其数据报告格式;
  • 规范化Input处理器(Handler)的定义及其回调实现;
  • 为Input设备和Input处理器提供核心服务;
  • 提供标准化的用户空间接口。

图1 Input事件处理整体框图

图1展示了Input事件处理的整体框架。本文将围绕这张图详细介绍Input子系统的各个方面,包括定义、初始化、注册与匹配、事件传递以及与用户空间的交互。由于个人理解有限,若有不当之处,敬请指正。

一.Input子系统相关定义

在之前的内容中,我们提到了Input Device、Input Handler等名词,但还没有详细说明。本节将介绍Input子系统中的几个重要结构体,以便于后续的理解。

1. Input_dev

struct input_dev用于抽象所有输入设备。由于不同设备的事件报告形式和内容存在差异,input_dev结构体必须包含这些差异化的内容。它的结构设计为通用部分(x)加上特定设备的差异部分(y1、y2、y3等)。因此,在实际的设备驱动开发中,只需填充相关的成员即可定义一个完整的input_dev。具体成员定义如下图所示:

2. Input_handler

struct input_handler用于抽象事件处理。不同输入设备的事件处理方式可能不同,Linux内核通过抽象input_handler结构体来统一事件处理流程,而具体的处理细节则通过回调函数实现。该结构体的主要职责包括匹配、建立连接、事件传递和过滤等,具体成员如下图所示:

3. Input_handle

在抽象了input_devinput_handler后,我们了解到一个input_dev可能会报告多个事件,并由多个input_handler处理。为管理这种多对多的关系,input_handle作为桥梁,连接了设备和处理器。图2展示了这种关系:

图2 设备与处理器示意图

input_handle的定义相对简单,具体成员如下:

二.Input子系统相关流程

主要流程包括Input核心初始化、输入设备注册、处理器注册、设备与处理器匹配,以及事件传递。

1. Input核心初始化

Input核心通过sybsys_initcall注册,设定启动优先级,以确保其初始化早于设备和处理器的注册。初始化过程中主要完成以下任务:

  1. 注册input类;
  2. 创建proc文件,用于查看处理器和设备信息;
  3. 注册字符设备,主设备号为13。

2. Input_dev注册

通常,输入设备驱动程序需要处理设备的控制和响应报告,而响应报告通过注册的input_dev来完成。在输入设备驱动的初始化过程中,通过调用input_register_device函数完成input_dev的注册,具体流程如下:

3. Input_handler注册

与设备注册相比,处理器的注册相对简单。填充input_handler结构体后,直接调用input_register_handler函数即可完成注册,具体流程如下:

4. Input_handler与Input_dev匹配

input_handlerinput_dev的匹配通过调用input_attach_handler函数完成。配对成功后,input_devinput_handlerinput_handle之间的关系如图3所示。设备驱动和事件处理层驱动均可通过input_handle访问相应的对象。匹配流程如下:

图3 input_handle与所有者关系图

如果handler中没有实现match回调,则根据input_dev中的ID与input_handlerid_table中的ID进行匹配。例如,evdev_handler可以匹配所有Input设备,因此所有Input设备都可以通过/dev/input/event*路径获取原始数据。

在匹配成功后,将调用connect回调,例如evdev_handler中的connect实现如下:

5. Input事件传递

不同设备上报的Input事件格式不同,例如触摸屏事件通常包含手指ID、x坐标和y坐标等信息。图4展示了B协议的

报点格式(A协议不需要ID,会在InputReader中重新分配)。

图4 触摸屏报点事件格式

每个事件通过input_event接口上报。在判断事件类型是否支持后,调用input_handle_event函数处理事件:

该接口中,根据typecode判断事件的disposition。当dispositionINPUT_PASS_TO_DEVICE时,事件传递给设备本身处理;当为INPUT_PASS_TO_HANDLERS时,事件传递给处理器处理,通常将事件存储在设备的vals数组中。只有当dispositionINPUT_FLUSH或事件达到数组容量极限时,事件才会传递给处理层(在input_sync时,disposition值为INPUT_FLUSH)。

input_get_disposition函数根据typecode判定事件的disposition,主要关心EV_SYNEV_KEYEV_ABS事件。在EV_KEY事件中,如果设置了按键自动重发并且当前事件与上次事件不同,则会将事件传递给处理层。

在收到同步事件或事件缓冲区接近满载时,会同步事件,input_pass_values函数将事件传递给相应的处理器。如果输入设备支持EV_REP事件,则会设置定时器自动重发按键事件。

所有Input设备都会与evdev_handler匹配,假设匹配的处理器为evdev_handler,则events指向的函数为evdev_events

evdev_pass_values函数将传入的所有事件存储在client->buffer中;kill_fasync函数发送通知事件,告知上层client->buffer中有数据可读。

6. Input事件传递到用户空间

当应用层或框架层调用read函数读取/dev/input/event*文件时,会调用evdev_read函数返回数据。event_fetch_next_event函数判断client->buffer这个循环缓冲区中的头尾指针是否相等。如果不相等,则从缓冲区中取出一个input_event类型的事件放入event中。input_event_to_user函数将事件拷贝到应用层,input_event_size函数用于获取input_event事件的大小,循环复制client->buffer中的事件到应用层的缓冲区中。

那么,谁会来打开文件读取这些事件呢?一种是getevent工具,另一种是Android框架层的inputflinger服务。inputflinger服务主要创建两个线程:InputReaderInputDispatcherInputReader负责处理底层事件,通过eventHub读取所有事件,然后通过设备属性或事件特征找到相应的映射器(mapper)将底层事件转换为Android设计的事件类型;InputDispatcher负责与窗口交互,将事件派发给对应的窗口。

参考文献

  1. https://www.cnblogs.com/lifexy/p/7542989.html
  2. https://blog.csdn.net/qq_39937242/article/details/82631165
  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新程序圆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值