概述
Android Input框架梳理
Android input子系统框架
触摸屏驱动上传数据通过input子系统上报,在分析驱动之前,先来看一下Android Input子系统的整体框架(图1)。Android系统基于Linux内核实现,内核作为整个操作系统的核心,对下,它负责整个硬件的驱动、实现对硬件器件的控制管理;对上,它提供各种系统所需的核心功能。Android系统支持的输入设备较多,如按键、触摸屏、手柄等,面对种类繁杂的输入设备,内核通过抽象化的方式来使得各输入设备的的核心处理流程统一化,细节处理流程差异化(通过不同类型的回调实现),这就是Input子系统所要完成的内容,总结来说,它在内核中主要作用为:
- 规范化Input Device的定义方式及其数据的上报格式;
- 规范化Input Handler的定义方式及其需要实现的回调;
- 为Input Device和Input Handler提供核心服务;
- 提供标准化用户空间接口;
从图1可以清楚的看到,硬件数据是如何一层层的往上传递,经过kernel和framework层,最终到达app应用,下面将分别从kernel和framework层分析数据传输上报过程。
kernel层
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。
驱动层
对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
将底层的硬件输入转化为统一事件形式,向输入核心(Input Core)汇报。
触摸屏驱动的位置就是对应图1中的Touchscreen Driver的位置,按照input设备驱动注册流程可以推断驱动里面做了以下事情:
- 调用input_allocate_device()函数分配了一个 input_dev 结构体;
- 设置 input_dev 所支持的事件类型;
- 调用 input_register_device()函数把input_dev注册到input core
input_dev 结构体定义如下所示:
/* include/linux/input.h */
struct input_dev {
const char *name; /* 设备名称 */
const char *phys; /* 设备在系统中的路径 */
const char *uniq; /* 设备唯一id */
struct input_id id; /* input设备id号 */
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 设备支持的事件类型,主要有EV_SYNC,EV_KEY,EV_KEY,EV_REL,EV_ABS等*/
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键所对应的位图 */
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标对应位图 */
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 决定左边对应位图 */
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 支持其他事件 */
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /* 支持led事件 */
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; /* 支持声音事件 */
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 支持受力事件 */
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /* 支持开关事件 */
unsigned int hint_events_per_packet; /* 平均事件数*/
unsigned int keycodemax; /* 支持最大按键数 */
unsigned int keycodesize; /* 每个键值字节数 */
void *keycode; /* 存储按键值的数组的首地址 */
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke, unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke);
struct ff_device *ff; /* 设备关联的反馈结构,如果设备支持 */
unsigned int repeat_key; /* 最近一次按键值,用于连击 */
struct timer_list timer; /* 自动连击计时器 */
int rep[REP_CNT]; /* 自动连击参数 */
struct input_mt *mt; /* 多点触控区域 */
struct input_absinfo *absinfo; /* 存放绝对值坐标的相关参数数组 */
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; /* 反应设备当前的案件状态 */
unsigned long led[BITS_TO_LONGS(LED_CNT)]; /* 反应设备当前的led状态 */
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; /* 反应设备当前的声音状态 */
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; /* 反应设备当前的开关状态 */
int (*open)(struct input_dev *dev); /* 第一次打开设备时调用,初始化设备用 */
void (*close)(struct input_dev *dev); /* 最后一个应用程序释放设备事件,关闭设备 */
int (*flush)(struct input_dev *dev, struct file *file); /* 用于处理传递设备的事件 */
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); /* 事件处理函数,主要是接收用户下发的命令,如点亮led */
struct input_handle __rcu *grab; /* 当前占有设备的input_handle */
spinlock_t event_lock; /* 事件锁 */
struct mutex mutex; /* 互斥体 */
unsigned int users; /* 打开该设备的用户数量(input_handle) */
bool going_away; /* 标记正在销毁的设备 */
struct device dev; /* 一般设备 */
struct list_head h_list; /* 设备所支持的input handle */
struct list_head node; /* 用于将此input_dev连接到input_dev_list */
unsigned int num_vals; /* 当前帧中排队的值数 */
unsigned int max_vals; /* 队列最大的帧数*/
struct input_value *vals; /* 当前帧中排队的数组*/
bool devres_managed; /* 表示设备被devres 框架管理,不需要明确取消和释放*/
};
usbtouchscreen驱动
我们以kernel中自带的usbtouchscreen驱动为例,源码路径是/kernel/drivers/input/touchscreen/usbtouchscreen.c,先来看驱动注册过程:
static struct usb_driver usbtouch_driver = {
.name = "usbtouchscreen", //driver name
.probe = usbtouch_probe, //probe接口,用于总线上匹配检测到这个驱动对应的设备之后,/kernel/drivers/usb/core/driver.c中的usb_probe_interface调用到我们这个驱动的接口
.disconnect = usbtouch_disconnect, //与probe相反,断开的时候调用
.suspend = usbtouch_suspend, //usb 设备挂起
.resume = usbtouch_resume, // 和上面挂起相反,唤醒
.reset_resume = usbtouch_reset_resume, // 重置唤醒
.id_table = usbtouch_devices, //支持的设备ID表
.supports_autosuspend = 1,
};
module_usb_driver(usbtouch_driver);
上述usb_driver结构体中的函数是USB设备驱动中与USB相关的部分,而USB只是一个总线,USB设备驱动真正的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。因此USB设备驱动包含其作为总线上挂载设备的驱动和本身所属设备类型的驱动两部分。usb_driver中的id_table 这个变量,代表支持的设备id列表,数据类型为:
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* not matched against */
kernel_ulong_t driver_info;
};
当USB核心检测到某个设备的属性和某个驱动程序的usb_device_id结构体所携带的信息一致时,这个驱动的probe()就被执行。拔掉设备或者卸载驱动模块后,USB核心就执行disconnect()函数。
static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
/* ignore the HID capable devices, handled by usbhid */
{USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
{USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},
...
#endif
...
};
其中可以看到 两个字节的十六进制数字,第一个代表idVendor 厂商ID,idProduct 产品ID ,这两个一般作为设备的标识。像上面的usbtouch_devices的数组中driver_info 设置为枚举值:
/* device types */
enum {
DEVTYPE_IGNORE = -1,
DEVTYPE_EGALAX,
DEVTYPE_PANJIT,
DEVTYPE_3M,
DEVTYPE_ITM,
DEVTYPE_ETURBO,
DEVTYPE_GUNZE,
DEVTYPE_DMC_TSC10,
DEVTYPE_IRTOUCH,
DEVTYPE_IDEALTEK,
DEVTYPE_GENERAL_TOUCH,
DEVTYPE_GOTOP,
DEVTYPE_JASTEC,
DEVTYPE_E2I,
DEVTYPE_ZYTRONIC,
DEVTYPE_TC45USB,
DEVTYPE_NEXIO,
};
那么这些driver 的真正的info保存在哪里呢? 在注册的时候,现在只是注册上去一个枚举数字而已,真正有设备识别到的时候这些个枚举值就起到作用了,在usbtouch_probe里面有使用到的地方。
static int usbtouch_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
struct usbtouch_usb *usbtouch;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
struct usbtouch_device_info *type;
int err = -ENOMEM;
/* some devices are ignored */
if (id->driver_info == DEVTYPE_IGNORE) //忽略的设备类型
return -ENODEV;
endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); //获取端点描述符数组指针
if (!endpoint)
return -ENXIO;
usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL); //分配usbtouch_usb结构体对象内存
input_dev = input_allocate_device(); //分配输入设备对象内存
if (!usbtouch || !input_dev) //分配不成功退出
goto out_free;
type = &usbtouch_dev_info[id->driver_info]; //根据id的driver_info信息获取全局usbtouch_dev_info数组项,这里就用到了上面说到的枚举值了, //真正的info 是放在这个数组里面的
usbtouch->type = type; //指定usbtouch_dev_info
if (!type->process_pkt) //若usbtouch_dev_info不存在process_pkt方法
type->process_pkt = usbtouch_process_pkt; //则默认设置为usbtouch_process_pkt
usbtouch->data = usb_alloc_coherent(udev, type->rept_size,GFP_KERNEL, &usbtouch->data_dma); //分配缓冲区
if (!usbtouch->data)
goto out_free;
if (type->get_pkt_len) { //若usbtouch_dev_info存在get_pkt_len方法
usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL); //则要根据rept_size分配usb_touch_usb对象缓冲区
if (!usbtouch->buffer)
goto out_free_buffers;
}
usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); //分配urb
if (!usbtouch->irq) {
dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
goto out_free_buffers;
}
usbtouch->interface = intf; //设置usb_touch_usb的usb接口
usbtouch->input = input_dev;//捆绑usb_touch_usb和输入设备
if (udev->manufacturer) //存在工厂名则设置工厂名
strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
if (udev->product) { //存在产品名则设置产品名
if (udev->manufacturer)
strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
}
if (!strlen(usbtouch->name)) //若不存在工厂名和产品名
snprintf(usbtouch->name, sizeof(usbtouch->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys)); //设置usb设备路径
strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
input_dev->name = usbtouch->name; //设置输入设备名
input_dev->phys = usbtouch->phys; //设置输入设备路径
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev; //设置usb设备为输入设备的父设备
input_set_drvdata(input_dev, usbtouch);
input_dev->open = usbtouch_open; //设置输入设备的open方法
input_dev->close = usbtouch_close; //设置输入设备的close方法
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); //按键和绝对位移事件
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //触摸按键
input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0); //绝对x坐标位移
input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0); //绝对y坐标位移
if (type->max_press)
input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,type->max_press, 0, 0);
if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT) //中断传输方式
usb_fill_int_urb(usbtouch->irq, udev,
usb_rcvintpipe(udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);
else //bulk传输方式
usb_fill_bulk_urb(usbtouch->irq, udev,
usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch);//初始化urb的回调函数为 usbtouch_irq
usbtouch->irq->dev = udev; //urb和usb设备捆绑
usbtouch->irq->transfer_dma = usbtouch->data_dma; //传输数据dma地址缓冲区
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //传输标志物dma映射传输
/* device specific allocations */
if (type->alloc) { //usbtouch_dev_info对象存在alloc方法
err = type->alloc(usbtouch); //则调用该方法
if (err) {
dbg("%s - type->alloc() failed, err: %d", __func__, err);
goto out_free_urb;
}
}
/* device specific initialisation*/
if (type->init) { //usbtouch_dev_info对象存在初始化方法
err = type->init(usbtouch); //则调用该初始化方法
if (err) {
dbg("%s - type->init() failed, err: %d", __func__, err);
goto out_do_exit;
}
}
err = input_register_device(usbtouch->input); //注册输入设备
if (err) {
dbg("%s - input_register_device failed, err: %d", __func__, err);
goto out_do_exit;
}
usb_set_intfdata(intf, usbtouch);
if (usbtouch->type->irq_always) { //usbtouch_dev_info对象存在irq_always方法
/* this can't fail */
usb_autopm_get_interface(intf); //电源唤醒
err = usb_submit_urb(usbtouch->irq, GFP_KERNEL); //提交urb
if (err) {
usb_autopm_put_interface(intf); //电源挂起
err("%s - usb_submit_urb failed with result: %d",
__func__, err);
goto out_unregister_input;
}
}
return 0;
out_unregister_input:
input_unregister_device(input_dev);
input_dev = NULL;
out_do_exit:
if (type->exit)
type->exit(usbtouch);
out_free_urb:
usb_free_urb(usbtouch->irq);
out_free_buffers:
usbtouch_free_buffers(udev, usbtouch);
out_free:
input_free_device(input_dev);
kfree(usbtouch);
return err;
}
usb请求块(usb request block,urb)是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构,是usb主机与设备之间传输数据的封装。一个urb包含了执行usb传输所需要的所有信息。当要进行数据传输时,需要分配一个urb结构体,对其进行初始化,然后将其提交给usb核心。usb核心对urb进行解析,将控制信息提交给主机控制器,由主机控制器负责数据到设备的传输。这时,驱动程序只需等待,当数据回传到主机控制器后,会转发给usb核心,唤醒等待的驱动程序,由驱动程序完成剩下的工作。
urb的处理流程如上图所示,可以从usbtouch_probe中可以看到驱动通过usb_alloc_urb创建了一个urb,并且将其通过usb_fill_bulk_urb进行初始化,但是并没有立刻将该urb通过usb_submit_urb()提交给USB核心,而且等上层打开input设备节点的时候才将urb提交:
static int usbtouch_open(struct input_dev *input)
{
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
int r;
usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface);
r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0;
if (r < 0)
goto out;
mutex_lock(&usbtouch->pm_mutex);
if (!usbtouch->type->irq_always) {
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
r = -EIO;
goto out_put;
}
}
usbtouch->interface->needs_remote_wakeup = 1;
usbtouch->is_open = true;
out_put:
mutex_unlock(&usbtouch->pm_mutex);
usb_autopm_put_interface(usbtouch->interface);
out:
return r;
}
在usbtouch_probe 中通过枚举值driver_info在usbtouch_dev_info数组中找到相对于的info信息,不同的usbtouchscreen 注册的时候就是注册了一个枚举值,这个值就是usbtouch_dev_info 数组的第几元素。
static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
[DEVTYPE_EGALAX] = {
.min_xc = 0x0,
.max_xc = 0x07ff,
.min_yc = 0x0,
.max_yc = 0x07ff,
.rept_size = 16,
.process_pkt = usbtouch_process_multi,//用于中断回调函数,用于处理中断,得到input的event,上传数据
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data, //用于中断回调函数,用于读取数据
},
#endif
...
#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
[DEVTYPE_IRTOUCH] = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.rept_size = 8,
.read_data = irtouch_read_data,
},
#endif
...
}
在usbtouch_probe 中可以看到,如果枚举值在usbtouch_dev_info数组中没有定义process_pkt,则使用默认usbtouch_process_pkt,其主要在中断URB的完成函数usbtouch_irq中被调用,usbtouch_irq里面会对urb->status进行判断,等于0则代表成功,反之则失败:
static void usbtouch_irq(struct urb *urb)
{
struct usbtouch_usb *usbtouch = urb->context;
struct device *dev = &usbtouch->interface->dev;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* this urb is timing out */
dev_dbg(dev,
"%s - urb timed out - was the device unplugged?n",
__func__);
return;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
case -EPIPE:
/* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %dn",
__func__, urb->status);
return;
default:
dev_dbg(dev, "%s - nonzero urb status received: %dn",
__func__, urb->status);
goto exit;
}
usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
exit:
usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(dev, "%s - usb_submit_urb failed with result: %dn",
__func__, retval);
}
接下进到默认的usbtouch_process_pkt来看一下,通过type->read_data将pkt里面的数据进行处理,这些处理的细节跟具体硬件厂商或者协议有关,如下egalax_read_data所示,将ptk的数据转换成坐标和触摸信息,然后通过input子系统将数据上传。
static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
unsigned char *pkt, int len)
{
struct usbtouch_device_info *type = usbtouch->type;
if (!type->read_data(usbtouch, pkt))
return;
input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
if (swap_xy) {
input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
} else {
input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
}
if (type->max_press)
input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
input_sync(usbtouch->input);
}
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
return 0;
dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
dev->touch = pkt[0] & 0x01;
return 1;
}
input_report_abs最后还是通过input_even上报事件,大致调用流程如图4所示:
linux input事件读取流程如图5所示,当应用层或框架层调用read函数读取/dev/input/event*文件时,会调用evdev_read返回数据,其中event_fetch_next_event是判断client->buffer这个循环缓冲区中的头尾指针是否相等(相等时buffer中没有数据),不相等时取出一个input_event类型的事件放入到event中。如果没有数据,进程会睡眠,那由谁来唤醒呢?当设备驱动层调用input_event上报事件调用相应的event函数进行事件写入时,是会唤醒阻塞等待的进程的。input_event_to_user函数是将此事件copy到应用层,input_event_size函数是用来获取一个input_event事件的大小,循环复制client->buffer中的事件到应用层的buffer中。
Input Core
对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
它承上启下:
- 为驱动层提供输入设备注册与操作接口,如:input_register_device;
- 通知事件处理层对事件进行处理。
input子系统的核心层维护着两条重要的链表,每当一个新的设备或者一个新的事件驱动被系统加载,都会扫描整个链表,并调用函数input_match_device进行配对。
1. static LIST_HEAD(input_dev_list); //记录所有的输入设备
2. static LIST_HEAD(input_handler_list); //记录所有的事件驱动
上面说到的input设备驱动最后会调用input_register_device进行注册,这个函数是属于核心层提供给设备驱动层的接口(driver/input/input.c)
input_register_device(input); /* 注册该设备 */
...... /* 各种填充input里面的变量 */
device_add(&dev->dev); /* 经过此步骤后sysfs的class里面的input下面会多出来一个inputx,里面展示的name等就是前面填充和初始化的 */
list_add_tail(&dev->node, &input_dev_list); /* 增加该dev到核心层维护的dev链表中 */
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); /* 尝试匹配handler */
input_match_device(handler, dev) /* 匹配过程(算法) */
handler->connect(handler, dev, id); /* 匹配上了则连接两者到handle,并创建设备(假设匹配到了evdev) */
....... /* 分配次设备号,申请一个struct evdev,并初始化里面的链表,等待队列等等 */
input_register_handle(&evdev->handle); /* 绑定dev到handle,handler在初始化这里以及绑定了 */
evdev_install_chrdev(evdev); /* 把该evdev添加到evdev维护的数组中 */
device_add(&evdev->dev); /* 在sysfs的 class里的input文件夹下创建eventx,为应用层提供接口 */
事件处理层
主要是和用户空间交互。(Linux中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下会生成相应的设备文件,这些操作在输入子系统中由事件处理层完成)。
实现设备驱动核心工作是:向系统报告按键等输入事件(event,通过input_event结构描述),不再需要关心文件操作接口。驱动报告事件经过inputCore和Eventhandler到达用户空间。
事件处理层会进行handler的注册,注册函数流程如下:
input_register_handler(&evdev_handler);
input_table[handler->minor >> 5] = handler; /* 注册该handler类型 */
list_add_tail(&handler->node, &input_handler_list); /* 把该handler加入到核心层维护的handler链表 */
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); /* 尝试匹配dev */
input_match_device(handler, dev) /* 匹配过程(算法) */
handler->connect(handler, dev, id); /* 匹配上了则连接两者到handle */
....... /* 分配次设备号,申请一个struct evdev,并初始化里面的链表,等待队列等等 */
input_register_handle(&evdev->handle); /* 绑定dev到handle,handler在初始化这里以及绑定了 */
evdev_install_chrdev(evdev); /* 把该evdev添加到evdev维护的数组中 */
device_add(&evdev->dev); /* 在sysfs的 class里的input文件夹下创建eventx,为应用层提供接口 */
Input_handler与input_dev的注册最终都会调用input_attach_handler完成自己与“相亲对象”的配对,配对完成后input_dev、input_handler、input_handle之间的关系如图6所示,设备驱动和事件处理层驱动都可以通过自身访问到input_handle,然后通过input_handle访问到自己的“对象”。
/* include/linux/input.h */
struct input_handler {
void *private; /* 存放handle数据 */
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;
const char *name; /* 名字 */
const struct input_device_id *id_table; /* input_dev匹配用的id */
struct list_head h_list; /* 用于链接和handler相关的handle,input_dev与input_handler配对之后就会生成一个input_handle结构 */
struct list_head node; /* 用于将该handler链入input_handler_list,链接所有注册到内核的所有注册到内核的事件处理器 */
};
struct input_handle {
void *private; /* 数据指针 */
int open; /* 打开标志,每个input_handle 打开后才能操作 */
const char *name; /* 设备名称 */
struct input_dev *dev; /* 指向所属的input_dev */
struct input_handler *handler; /* 指向所属的input_handler */
struct list_head d_node; /* 用于链入所指向的input_dev的handle链表 */
struct list_head h_node; /* 用于链入所指向的input_handler的handle链表 */
};
从上面可以知道,不管是input_register_handler还是input_register_device都是从input_dev_list和input_handler_list链表中找到匹配然后生成相应的input节点供用户调用,关系如图7所示:
framework层
当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件。可见,输入系统在整个过程起到承上启下的衔接作用。
framework Input模块的主要组成:
- Native层的InputReader负责从EventHub取出事件并处理,再交给InputDispatcher;
- Native层的InputDispatcher接收来自InputReader的输入事件,并记录WMS的窗口信息,用于派发事件到合适的窗口;
- Java层的InputManagerService跟WMS交互,WMS记录所有窗口信息,并同步更新到IMS,为InputDispatcher正确派发事件到ViewRootImpl提供保障;
EventHub
Eventhub是底层 events处理的枢纽,并向上层传递。先看构造函数:
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
//创建epoll
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
mINotifyFd = inotify_init();
//此处DEVICE_PATH为"/dev/input",监听该设备路径
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
//添加INotify到epoll实例
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
int wakeFds[2];
result = pipe(wakeFds); //创建管道
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
//将pipe的读和写都设置为非阻塞方式
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32 = EPOLL_ID_WAKE;
//添加管道的读端到epoll实例
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
...
}
EventHub在初始化时,构造函数中创建了两个Fd, mEpollFd和mINotifyFd。其中mINotifyFd用于监听设备节点是否有设备文件的增删,将mINotifyFd注册到 mEpollFd中,当发生新的设备增删,设备节点下的设备文件也会随之增删,就会通知 mEpollFd有新的可读的设备增删事件,通知 Eventhub对设备进行处理。而 mEpollFd监听是否有可读事件,创建管道,将读端交给epoll,写端交给 InputReader来处理事件。
InputReader会在每次loop时,调用 Eventhub的 getevents来获取Input事件:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
AutoMutex _l(mLock); //加锁
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer; //原始事件
size_t capacity = bufferSize; //容量大小为256
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
...
//第一次为ture
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked(); //打开“/dev/input”目录后,将其注册到epoll的监控队列中
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED; //添加设备的事件
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
...
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
//从mPendingEventItems读取事件项
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
...
//获取设备ID所对应的device
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
//从设备不断读取事件,放入到readBuffer
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
deviceChanged = true;
closeDeviceLocked(device);//设备已被移除则执行关闭操作
} else if (readSize < 0) {
...
} else if ((readSize % sizeof(struct input_event)) != 0) {
...
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
//获取readBuffer的数据
struct input_event& iev = readBuffer[i];
//将input_event信息, 封装成RawEvent
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+ nsecs_t(iev.time.tv_usec) * 1000LL;
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
mPendingEventIndex -= 1;
break;
}
}
}
...
}
...
mLock.unlock(); //poll之前先释放锁
//等待input事件的到来
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
mLock.lock(); //poll之后再次请求锁
if (pollResult < 0) { //出现错误
mPendingEventCount = 0;
if (errno != EINTR) {
usleep(100000); //系统发生错误则休眠1s
}
} else {
mPendingEventCount = size_t(pollResult);
}
}
return event - buffer; //返回所读取的事件个数
}
第一次 getEventst时,打开/dev/input/目录下的Input设备,并将其注册到epoll的监控队列中。这样一旦对应设备上有可读的Input事件,则 epoll_wait()就会返回,并带回 deviceid,找到具体的 device。整个事件的获取中,除了Input事件,设备的打开关闭等信息,也要包装成 event,上报给 InputReader。简单理解, Eventhub就是 InputReader用于打开和关闭inpu设备,监听和读取Input事件的对象。
InputReader
InputReader和InputDispatcher是在Inputmanager构造方法中被初始化的
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
//创建InputDispatcher对象
mDispatcher = new InputDispatcher(dispatcherPolicy);
//创建InputReader对象
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
void InputManager::initialize() {
//创建线程“InputReader”
mReaderThread = new InputReaderThread(mReader);
//创建线程”InputDispatcher“
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
值得留意的是, InputDispatcher作为参数传入了 InputReader的构造方法。再看 InputReader的构造方法
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);//这个listener对应的就是InputDispatcher
{
AutoMutex _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
}
}
初始化了一个 QueuedInputListener,它接收 Inputlistenerinterface调用可知InputListenerInterface其实就是 InputDispatcher, QueuedInputListener只是作为InputDispatcher的Wrapper。
在 InputManager执行start的时候 InputReaderThread->run()
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
接着执行 mReader->loopOnce();
void InputReader::loopOnce() {
...
{
AutoMutex _l(mLock);
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
timeoutMillis = 0;
...
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
}
//从EventHub获取事件,其中EVENT_BUFFER_SIZE = 256
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) { //处理事件
processEventsLocked(mEventBuffer, count);
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
...
} // release lock
if (inputDevicesChanged) { //输入设备发生改变
mPolicy->notifyInputDevicesChanged(inputDevices);
}
//发送事件到InputDispatcher
mQueuedListener->flush();
}
EventHub->getEvents上面已经分析了,主要就是获取 kernel的 event,这里事件不仅包括 input,还包括输入设备的add/remove等相关事件。
void InputReader::processEventsLocked(const RawEvent*rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1; //同一设备的事件打包处理
}
//数据事件的处理
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
//设备添加
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
//设备移除
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
//设备扫描完成
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false);
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
processEventsLocked根据返回的那些事件进行处理,包括对输入设备的增加和移出,以及输入事件的处理。先来说说设备增加的过程:
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex >= 0) {
return; //已添加的相同设备则不再添加
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
device->configure(when, &mConfig, 0);
device->reset(when);
mDevices.add(deviceId, device); //添加设备到mDevices
...
}
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, uint32_t classes) {
//创建InputDevice对象
InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
controllerNumber, identifier, classes);
...
//获取键盘源类型
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
}
if (classes & INPUT_DEVICE_CLASS_DPAD) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
keyboardSource |= AINPUT_SOURCE_GAMEPAD;
}
//添加键盘类设备InputMapper
if (keyboardSource != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
}
//添加鼠标类设备InputMapper
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper(device));
}
//添加触摸屏设备InputMapper
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
...
return device;
}
该方法主要功能:
- 创建InputDevice对象,将InputReader的mContext赋给InputDevice对象所对应的变量
- 根据设备类型来创建并添加相对应的InputMapper,同时设置mContext.
input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:
- 键盘类设备:KeyboardInputMapper
- 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
- 鼠标类设备:CursorInputMapper
介绍完设备增加过程,继续回到除了设备的增删,更常见事件便是数据事件,那么接下来介绍数据事件的处理过程。
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
...
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
return; //可忽略则直接返回
}
device->process(rawEvents, count);
}
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
}
createDeviceLocked创建设备并添加InputMapper,提到会有多种InputMapper。inputMapper的主要作用是将RawEvent事件转换成对应的Notifyargs,并将其加入到InputRead::QueueListener::argsQueue队列中,后面调用**InputReader::QueueListener::flush()**函数,处理队列中的事件消息,该函数里面会调用Notifyargs::notify函数,将事件消息转换成对应的EventEntry再转交给InputDispatcher。
InputReader整个过程涉及多次事件封装转换,其工作流程如图8所示,其主要工作核心是以下三大步骤:
- getevents时:打开"/dev/ input/"目录下的Input设备,并将其注册到epoll的监控队列中。这样一旦对应设备上有可读的 Input事件,则 epoll_wait()就会返回,并带回 deviceid,找到具体的 device。整个事件的获取中,除了 input事件,设备的打开关闭等信息,也要包装成 event,上报给 InputReader
- processEventsLocked: 对事件进行加工, 转换RawEvent -> NotifyArgs
- QueuedListener->flush:将事件发送到InputDispatcher线程, 转换NotifyArgs -> EventEntry
InputReader线程不断循环地执行InputReader.loopOnce(), 每次处理完生成的是EventEntry(比如KeyEntry, MotionEntry), 加入到InputDispatcher的mInboundQueue队列,再唤醒InputDispatcher线程,接下来的工作就交给InputDispatcher线程了,整体事件获取流程如图9所示。
InputDispatcher
先来看一下InputDispatcher初始化
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
//创建Looper对象
mLooper = new Looper(false);
mKeyRepeatState.lastKeyEntry = NULL;
//获取分发超时参数
policy->getDispatcherConfiguration(&mConfig);
}
该方法主要工作:
- 创建属于自己线程的Looper对象;
- 超时参数来自于IMS,参数默认值keyRepeatTimeout = 500,keyRepeatDelay = 50。
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
AutoMutex _l(mLock);
//唤醒等待线程,monitor()用于监控dispatcher是否发生死锁
mDispatcherIsAliveCondition.broadcast();
if (!haveCommandsLocked()) {
//当mCommandQueue不为空时处理
dispatchOnceInnerLocked(&nextWakeupTime);
}
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
}
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis); //进入epoll_wait
}
整个过程不断循环地调用InputDispatcher的dispatchOnce()来分发事件,处理的事件主要分两种:一种是 command命令( Command是 mPolicy处理的具体事务,这个mPolicy就是Nativeinputmanager,最终对应的上层是 Inputmanagerservice),一种是 input event。每次 dispatchOnce,会优先执行前者。线程执行Looper->pollOnce
,进入epoll_wait等待状态,当发生以下任一情况则退出等待状态:
- timeout:到达 nextWakeupTime 时间,超时唤醒
- wake: 主动调用 Looper 的 wake() 方法(有输入事件注入派发队列中时)
- epol_wait() 监听的 fd 有 epoll_event 发生时唤醒
dispatchOnce 派发线程的一次循环包括如下三项工作:
- 进行一次事件派发。事件的派发工作仅当命令队列中没有命令时才会进行。派发工作会设置 nextWakeupTime,指明随后休眠时间的长短
- 执行命令列表中的命令。所谓的命令,不过是一个符合 command 签名的回调函数,可以通过 InputDispatcher::postCommandLocked() 函数将其追加到命令列表中
- 陷入休眠状态
可见派发线程的线程循环是比较清晰的,不过可能对 nextWakeupTime 的存在意义有一些费解。它对派发线程的执行过程有着举足亲重的作用。假如,当派发队列中最后一个事件派发完成后,nextWakeupTime 将被设置为 LONG_LONG_MAX,使之在新的输入事件或命令到来前休眠以节约资源。另外,有时因为窗口尚未准备好接受事件(如已经有一个事件发送给窗口,但此窗口尚未对其进行反馈),则可以放弃此事件的派发并设置 nextWakeupTime 为一个合理的时间点,以便在下次循环时再尝试派发。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now(); // 当前时间,也是后面 ANR 计时的起点
if (!mDispatchEnabled) { // 默认值为 false
resetKeyRepeatLocked(); // 重置上一个事件
}
// 分发被冻结, WMS 冻屏时会设置这个值,例如转屏
// InputDispatcher 有三种状态,分别是正常状态、冻结状态和禁用状态,
// 可以通过 InputDispatcher 的 setInputDispatchMode 函数来设置
if (mDispatchFrozen) { // 默认值为 false
return; // 如果 InputDispatcher 被冻结,则不进行派发操作,直接返回
}
// 对于特殊按键 HOME, END_CALL, APP_SWITCH 增加了 0.5s 超时时间
//在 enqueueInboundEventLocked() 的过程中已设置 mAppSwitchDueTime 等于 eventTime 加上 500ms
// 超时之后会丢弃其他即将处理的按键事件。即 isAppSwitchDue 为 true 的情况
// 目的是优化 app 切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件
// 如果 isAppSwitchDue 为 true,说明没有及时响应 HOME 键等操作
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
// 这样当 InputDispatcher 处理完分发事件后,会第一时间处理窗口切换操作
if (mAppSwitchDueTime < *nextWakeupTime) {
*nextWakeupTime = mAppSwitchDueTime;
}
// 如果还没有待分发的事件,去 mInboundQueue 中取出一个事件
if (!mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
if (!mPendingEvent) {
// 如果 mInboundQueue 为空,并且没有待分发的事件,就 return
return;
}
} else {
// 取队列 mInboundQueue 头部的 EventEntry 赋值给 mPendingEvent
mPendingEvent = mInboundQueue.dequeueAtHead();
}
......
resetANRTimeoutsLocked(); // 重置 ANR 信息
}
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED; // 默认值为不丢弃
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
// 当前事件不包含 POLICY_FLAG_PASS_TO_USER 的标志,则不合法,需要丢弃
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
// 当前事件被禁止分发,需要丢弃
dropReason = DROP_REASON_DISABLED;
}
......
switch (mPendingEvent->type) {// 判断事件的类型,有 key,motion 等
......
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEventLocked(typedEntry)) {
// 重置 mAppSwitchDueTime
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
// 没有及时响应窗口切换操作
// 由于 APP 切换需要丢弃非 HOME,ENDCALL,APP_SWITCH 之外的事件
dropReason = DROP_REASON_APP_SWITCH;
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
// 当前处理事件的时间减去事件产生的时间大于 10s 也需要丢弃
dropReason = DROP_REASON_STALE;// 事件过期
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;// 阻碍其他窗口获取事件
}
// 进一步分发按键事件,结果赋值给 done
// 无论是成功派发还是事件被丢弃,都返回 true,否则返回false
// 以便在下次循环时再次尝试此事件的派发
done = dispatchKeyLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
......
}
......
// 如果事件派发完成,则准备派发下一个事件
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
// 最后事件还是被丢弃
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked(); // 释放 pending 事件 mPendingEvent 等
// 立即开始下一次循环。如果此时派发队列为空,
// 下次循环调用此函数时会保持 nextWakeupTime 为
// LONG_LONG_MAX 并直接返回,使得派发线程进入无限期休眠
*nextWakeupTime = LONG_LONG_MIN; // 强制立刻执行轮询
}
}
dispatchOnceInnerLocked 主要功能:
1.如果派发队列 mInboundQueue 为空,则会使派发线程陷入无限期休眠状态
2.如果 mPendingEvent 为空,则从派发队列中取出一个并保存在 mPendingEvent 中
3.事件有可能因为某些原因被丢弃,被丢弃的原因保存在 dropReason 中
4.不同类型的事件使用不同的派发函数进行实际的派发动作。如 Motion 事件使用 dispatchMotionLocked() 函数进行派发,Key 事件使用 dispatchKeyLocked 函数进行派发
5.派发一个事件至少需要一次线程循环才能完成,事件的目标窗口有可能正在处理先前的一个输入事件,在窗口完成先前事件的处理并给予反馈之前,InputDispatcher 不会再向此窗口派发新事件。是否在下次循环继续尝试此事件的派发由派发函数的返回值决定
6.事件的派发是串行的,在排队首的事件完成派发或被丢弃之前,不会对后续的事件进行派发。理解 InputDispatcher 的这一特点非常重要
void InputDispatcher::dropInboundEventLocked(EventEntry* entry,
DropReason dropReason) {
const char* reason;
switch (dropReason) {
case DROP_REASON_POLICY:
reason = "inbound event was dropped because the policy consumed it";
break;
case DROP_REASON_DISABLED:
if (mLastDropReason != DROP_REASON_DISABLED) {
ALOGI("Dropped event because input dispatch is disabled.");
}
reason = "inbound event was dropped because input" +
" dispatch is disabled";
break;
case DROP_REASON_APP_SWITCH:
ALOGI("Dropped event because of pending overdue app switch.");
reason = "inbound event was dropped because of pending "+
"overdue app switch";
break;
case DROP_REASON_BLOCKED:
ALOGI("Dropped event because the current application is not "+
"responding and the user "
"has started interacting with a different application.");
reason = "inbound event was dropped because the current"+
" application is not responding "
"and the user has started interacting with a different application";
break;
case DROP_REASON_STALE:
ALOGI("Dropped event because it is stale.");
reason = "inbound event was dropped because it is stale";
break;
default:
return;
}
switch (entry->type) {
case EventEntry::TYPE_KEY: {
CancelationOptions options(
CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
......
}
}
前面的代码介绍了因为 DispatcherPolicy 认为事件不应派发给用户,以及 InputDispatcher 被停用而导致事件被丢弃。DropReason 枚举完整地描述了事件被丢弃的所有原因。
- DROP_REASON_POLICY:某些输入事件具有系统级的功能,例如 HOME 键、电源键、电话接听/挂断键等被系统处理,因此 DispatcherPolicy 不希望这些事件被窗口所捕获。当 InputDispatcher 在将输入事件放入派发队列前,向 DispatcherPolicy 询问此事件的派发策略时,DispatcherPolicy 会将 POLICY_FLAG_PASS_TO_USER 策略去掉。没有这个派发策略的事件对象会被丢弃
- DROP_REASON_APP_SWITCH:我们知道 InputDispatcher 的事件派发是串行的。因此在前一个事件的派发成功并得到目标窗口的反馈前,后续的输入事件都会被其阻塞。当某个窗口因程序缺陷而无法响应输入时,懊恼的用户可能会尝试使用 HOME 键退出这个程序。然而遗憾的是,由于派发的串行性,用户所按的 HOME 键在其之前的输入事件成功派发给无响应的窗口之前无法获得派发的机会,因此在 ANR 对话框弹出之前的 5 秒里,用户不得不面对无响应的应用程序欲哭无泪。为了解决这个问题,InputDispatcher 为 HOME 键设置了限时派发的要求。当 InputDispatcher 的 enqueueInboundEventLocked() 函数发现 HOME 键被加入派发队列后,便要求 HOME 键之前的所有输入事件在 0.5 秒(由 APP_SWITCH_TIMEOUT 常量定义)之前派发完毕,否则这些事件将都会被丢弃,使得 HOME 键至少能够在 0.5 秒内得到响应
- DROP_REASON_BLOCKED:和 APP_SWITCH 原因类似,如果是因为一个窗口无法响应输入事件,用户可能希望在其他窗口上进行点击,以尝试是否能够得到响应。因为派发的串行性,这次尝试会以失败而告终。为此,当 enqueueInboundEventLocked() 发现有窗口正阻塞着派发的进行,并且新入队的触摸事件的目标是另外一个窗口,则将这个新事件保存到 mNextUnblockedEvent 中。随后的 dispatchOnceInnerLocked() 会将此事件之前的输入事件全部丢弃,使得用户在其他窗口上进行点击的尝试可以立即得到响应
- DROP_REASON_DISABLED:因为 InputDispatcher 被禁用而使得事件被丢弃。setInputDispatchMode() 函数可以使得 InputDispatcher 在禁用、冻结与正常三种状态之间进行切换。禁用状态会使得所有事件被丢弃,冻结将会使得 dispatchOnceInnerLocked() 函数直接返回从而停止派发工作。InputDispatcher 的这三种状态的切换由 java 层的 IMS 提供接口,由 AMS 和 WMS 根据需要进行设置。例如,当手机进入休眠状态时,InputDispatcher 会被禁用,而屏幕旋转过程中,InputDispatcher 会被暂时冻结
- DROP_REASON_STALE:在 dispatchOnceInnerLocked() 函数准备对事件进行派发时,会先检查一下事件所携带的事件戳与当前时间的差距。如果事件过于陈旧(10 秒以上,由常量 STALE_EVENT_TIMEOUT 所指定),则此事件需要被抛弃
当事件幸运地避开所有上述原因之后,才能由 InputDispatcher 尝试派发。对 Key 事件来说,下一步是 dispatchKeyLocked() 函数。在这个函数中,InputDispatcher 将为事件寻找合适的目标窗口。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
......
if (entry->interceptKeyResult ==
KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
// case1: 当前时间小于唤醒时间,则进入等待状态
if (currentTime < entry->interceptKeyWakeupTime) {
if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
*nextWakeupTime = entry->interceptKeyWakeupTime;
}
return false; //直接返回 wait until next wakeup
}
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
entry->interceptKeyWakeupTime = 0;
}
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
//case2: 让 policy 有机会执行拦截操作
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
if (mFocusedWindowHandle != nullptr) {
commandEntry->inputChannel =
getInputChannelLocked(focusedWindowHandle->getToken());
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
return false; //直接返回 wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
} else if (entry->interceptKeyResult ==
KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
if (*dropReason == DROP_REASON_NOT_DROPPED) {
*dropReason = DROP_REASON_POLICY;
}
}
//case3: 如果需要丢弃该事件,则执行清理操作
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
mReporter->reportDroppedKey(entry->sequenceNum);
return true; //直接返回
}
// 目标窗口信息列表会存储在 inputTargets 中
Vector<InputTarget> inputTargets;
// case4: 寻找焦点
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
// 输入事件被挂起,说明找到了窗口并且窗口无响应
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false; //直接返回
}
setInjectionResultLocked(entry, injectionResult);
// 输入事件没有分发成功,说明没有找到合适的窗口
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true; // 直接返回
}
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
// 只有 injectionResult 是成功,才有机会执行分发事件
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
dispatchKeyLocked 函数,在以下场景中,有可能无法分发事件:
- 当前时间小于唤醒时间 (nextWakeupTime) 的情况
- policy 需要提前拦截事件的情况
- 需要丢弃事件的情况
- 寻找聚焦窗口失败的情况
如果成功跳过以上所有 case,则会继续调用 dispatchEventLocked,继续进行事件的分发。
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
// 向 mCommandQueue 队列添加 doPokeUserActivityLockedInterruptible 命令
pokeUserActivityLocked(eventEntry);
for (const InputTarget& inputTarget : inputTargets) {
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
// 找到目标连接
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}
这里会遍历 inputTargets,并调用 prepareDispatchCycleLocked 函数继续事件的分发。
需要重点关注参数 connection,它是用来描述窗口的连接,每一个窗口对应一个连接,这个连接就是用来将输入事件传递到目标窗口,connection 是在 registerInputChannel 的时候创建的。
其中 pokeUserActivityLocked(eventEntry) 方法,最终会调用到 Java 层的 PowerManagerService.java 中的 userActivityFromNative() 方法。 这也是 PMS 中唯一的 native call 方法。
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
if (inputChannel == nullptr) {
return -1;
}
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
sp<Connection> connection = mConnectionsByFd.valueAt(i);
if (connection->inputChannel->getToken() == inputChannel->getToken()) {
return i;
}
}
return -1;
}
根据 inputChannel 的 fd 从 mConnectionsByFd 队列中查询目标 connection。
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
if (connection->status != Connection::STATUS_NORMAL) {
return; //当连接已破坏,则直接返回
}
......
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
// 调用了六次 enqueueDispatchEntryLocked 函数,仅仅最后一个参数不同
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
// 当原先的 outbound 队列为空, 且当前 outbound 不为空的情况执行
startDispatchCycleLocked(currentTime, connection);
}
}
该方法主要功能:
- 根据 dispatchMode 来分别执行 DispatchEntry 事件加入队列的操作
- 当起初 connection.outboundQueue 等于空,经 enqueueDispatchEntryLocked 处理后,outboundQueue 不等于空情况下,则执行startDispatchCycleLocked() 方法
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return; // 分发模式不匹配,则直接返回
}
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
// 将 EventEntry 转换为 DispatchEntry, 加入 connection 的 outbound 队列
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
if (!connection->inputState.trackKey(keyEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
delete dispatchEntry;
return; // 忽略不连续的事件
}
break;
}
case EventEntry::TYPE_MOTION: {
......
}
......
}
......
// 添加到 connection 的 outboundQueue 队尾
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
enqueueDispatchEntryLocked 主要功能:
- 根据 dispatchMode 来决定是否需要加入 outboundQueue 队列
- 根据 EventEntry 来生成 DispatchEntry 事件
- 将 dispatchEntry 加入到 connection 的 outboundQueue 队列
执行到这里,其实等于做了一次搬运的工作,将 InputDispatcher 中 mInboundQueue 中的事件取出后,找到目标 window 后,封装成 dispatchEntry 加入到目标窗口对应的 connection 中的 outboundQueue 队列。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
// 当 Connection 状态正常,且 outboundQueue 不为空
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
dispatchEntry->deliveryTime = currentTime; // 设置 deliveryTime 时间
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// 核心动作:发布 Key 事件
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
......
}
// Check the result.
if (status) { // publishKeyEvent 失败情况
if (status == WOULD_BLOCK) {
if (connection->waitQueue.isEmpty()) {
// pipe 已满,但 waitQueue 为空. 不正常的行为
// 该方法最终会调用到 Java 层的IMS.notifyInputChannelBroken()
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
// 处于阻塞状态
connection->inputPublisherBlocked = true;
}
} else {
// 不正常的行为
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
}
// 从 outboundQueue 中取出事件,重新放入 waitQueue 队列
connection->outboundQueue.dequeue(dispatchEntry);
connection->waitQueue.enqueueAtTail(dispatchEntry);
}
}
startDispatchCycleLocked 主要功能:将目标窗口 connection 的 outboundQueue 队列中的 dispatchEntry 依次取出来,对于按键类型的输入事件会调用 connection->inputPublisher 的 publishKeyEvent 进行分发,分发成功之后会将 outboundQueue 队列中的 dispatchEntry 移除,并转移到 waitQueue 中。
status_t InputPublisher::publishKeyEvent(...) {
......
// 拿到输入事件的各种信息之后构造一个 InputMessage
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
// 通过 InputChannel 来发送消息
return mChannel->sendMessage(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
//异常情况,发送失败
return -error;
}
if (size_t(nWrite) != msgLength) {
return DEAD_OBJECT;
}
return OK;
}
sendMessage 的核心就是向 mFd 写入数据,mFd 是什么呢?mFd 其实就是一对 socket 的其中一个,InputChannel 在构造的时候是一对,对应了一对 socket,一个代表 “client” 端,一个代表 “server” 端,“server” 端被注册到了 InputDispatcher,“client” 端返回给了 APP 进程,InputDispatcher 和 APP 进程都会对自己的 socket 一端进行监听,所以 APP 进程和 InputDispatcher 就这样完成了通信。
APP 进程和 InputDispatcher 的通信过程:
- InputDispatcher线程调用InputPublisher的publishKeyEvent向UI主线程发送input事件;
- UI主线程接收到该事件后,调用InputConsumer的consumeEvents来处理该事件, 一路执行到ViewRootImpl.deliverInputEvent()方法;
- UI主线程经过一系列的InputStage来处理, 当事件分发完成,则会执行finishInputEvent()方法.再进一步调用InputConsumer::sendFinishedSignal 告知InputDispatcher线程该时事件已处理完成.
- InputDispatcher线程收到该事件后, 执行InputDispatcher::handleReceiveCallback();最终会调用doDispatchCycleFinishedLockedInterruptible()方法 ,将dispatchEntry事件从等待队列(waitQueue)中移除.
InputDispatcher 对事件的分发流程是相当复杂的,我们仅对最简单的按键类型事件进行分析,同时分析过程省略了对细节的处理,如图10所示,现在就对这个过程做一个总结:
- InputReader 线程,将驱动获取的原始输入事件封装为 NotifyKeyArgs 传递给了 InputDispatcher 线程,在放入 InputDispatcher 线程 mInboundQueue 队列之前会先将事件传递到 java 层的 PhoneWindowManager 中,没有被拦截的情况下才会将 NotifyKeyArgs 转换为 KeyEntry 并放入 mInboundQueue 队列,接着会唤醒 InputDispatcher 线程
- InputDispatcher 线程启动后,如果没有事件待处理就会进入了 Looper 休眠状态,等待输入事件的发生,被唤醒之后调用函数 dispatchOnceInnerLocked 处理事件,此函数在一些条件判断之后,发现没有丢弃事件,则会进一步调用 dispatchKeyLocked 函数
- dispatchKeyLocked 函数在分发之前,又会首先将按键事件传到 java 层的 PhoneWindowManager 的 interceptKeyBeforeDispatching 中,给个提前拦截的机会,如果没有被拦截则会通过 findFocusedWindowTargetsLocked 寻找目标焦点窗口
- findFocusedWindowTargetsLocked 会从两个容器 mFocusedWindowHandlesByDisplay 和 mFocusedApplicationHandlesByDisplay 中获得当前的焦点窗口和焦点应用,并且会对可能出现 ANR 的情况进行 ANR timeout 即 ANR 发生窗口的标记
- 如果 findFocusedWindowTargetsLocked 函数,返回结果为成功分发,则调用 dispatchEventLocked 函数继续分发输入事件,接着会将 KeyEntry 再转换为 DispatchEntry,并存入目标窗口连接 connection 的 outboundQueue 队列,然后调用 publishKeyEvent 继续分发
- publishKeyEvent 函数中,构造了描述输入事件信息的 InputMessage,并通过 InputChannel 向 “server” 端 socket 写入数据以唤醒 APP 进程的 socket “client” 端,自此输入事件成功从 InputDispatcher 跨进程发送到了 APP 进程
- 最后将 DispatchEntry 从目标窗口连接 connection 的 outboundQueue 队列中移除,并转移到目标窗口连接 connection 的 waitQueue 队列中
整个事件传输过程中,有三个重要队列:mInboundQueue,outboundQueue,waitQueue。
mInboundQueue 位于 InputDispatcher 线程,代表即将分发的输入事件,outboundQueue 位于目标窗口的 connection 中,代表即将要分发给目标窗口的输入事件,waitQueue 也位于目标窗口的 connection 中,表示等待目标窗口处理的输入事件。
Android input整体流程小结
整体流程框架如图11所示,当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,通过kernel input子系统往上报,EventHub利用Linux的inotify和epoll机制,监听设备事件:包括设备插拔及各种触摸、按钮事件等,InputManagerService的InputReader线程通过EventHub的getEvents就可以监听并获取该事件,预处理后发送给InputDispatcher线程,InputDispatcher找到目标窗口,通过Socket将事件发送到目标窗口,接着APP端被唤醒,找到目标窗口处理事件。
最后
以上就是专注飞鸟为你收集整理的Android Input框架梳理Android Input框架梳理的全部内容,希望文章能够帮你解决Android Input框架梳理Android Input框架梳理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复