【转载两篇关于android按键事件传递的文章,流程非常完整】Android按键事件传递流程(一)
2017-07-01 16:47
363 查看
做Android开发的少不了对触摸、按键事件进行处理,对于手机来说,主要是手势移动、触摸按下等,而TV主要通过遥控器、按键操作,按键事件不同于触摸事件,必须先获得焦点,然后才能移动、选择。
android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层,内核层传递过程不在本文研究范围内,本文主要对按键事件在Framework层、应用层的传递过程进行分析(本文基于Android5.1.1版本),带着问题出发:
1. Framework层如何获得输入事件
2. Framework层获得按键事件后如何处理、存储
3. Framework层如何分发、传递给应用层
4. Framework层服务端管道和应用程序客户端管道如何创建、注册
5. 应用层如何从Framework层接收按键事件
6. 应用层接收到按键事件后如何传递
7. 特殊按键如何处理
8. 总结
要分析输入系统,最好先从输入事件系统服务InputManagerService入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。
1.1 InputManagerService的初始化
Android系统启动时,Android部分第一个用户空间进程zygote(注:如果从Linux角度出发,第一个用户空间进程是init)首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerService、PowerManagerService、PackageManagerService、InputManagerService等,相关代码在SystemServer.Java的run方法中,与InputManagerService相关部分:
1.1.1 InputManagerService的构造方法
创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象
用Looper中的消息队列作为参数,调用本地方法nativeInit,返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。
把InputManagerInternal.class和LocalService对象作为一对映射添加到ArrayMap<Class<?>, Object>中
1.1.2 nativeInit方法实现
android_os_MessageQueue_getMessageQueue方法:
获得了C++层NativeMessageQueue对象,NativeMessageQueue对象是在创建Looper对象时创建的,在本博客Handler机制中已经分析过
创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;getLooper()获得c++层的Looper对象,在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中
NativeInputManager的构造函数:
把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法;创建c++层中全局变量mContextObj, mServiceObj;创建EventHub对象并作为参数创建InputManager对象。
1.1.3 EventHub.cpp的构造函数
初始化必要的变量,采用epoll机制系统调用接口epoll_create创建epoll对象,返回文件描述符mEpollFd,代表epoll对象;采用inotify机制监听文件或目录的移动、读取、写入或删除等事件,inotify机制主要包含2个系统调用接口:inotify_init, inotify_add_watch
inotify_init:创建一个inotify对象,如果成功,返回一个文件描述符,作为该对象;返回-1表示出错
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)
inotify_add_watch:把监控项添加到mINotifyFd对象的监控列表中,第二个参数DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;第三个参数是一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。IN_DELETE:当被监控目录内删除文件或目录时触发该事件;IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。
如果inotify_add_watch执行成功,返回一个非负监控描述符(假如为wd),代表被监控的项,如果将来不需要再监听了,可以使用inotify_rm_watch(fd, wd)删除所添加的监控项wd。
把inotify对象mINotifyFd添加到epoll对象的兴趣列表中,此处采用inotify与epoll机制结合起来检查文件
创建了管道对象读取端文件描述符mWakeReadPipeFd并添加到epoll对象的兴趣列表中
读到此处,提出2个问题:
Q1 epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait,猜测在代码某处肯定有epoll_wait,否则epoll机制无法工作
Q2 如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用
记住这两个问题,有助于在分析多进程控制时的代码走向
1.1.4 InputManager.cpp的构造器
第一个参数是刚创建的EventHub对象,第二、三个参数都是this,当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager
用这两个传递过去的对象类型作为参数分别创建了InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象
1.1.5 InputDispatcher的构造函数
把传递过来的NativeInputManager对象赋给mPolicy,该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型,然后又初始化了很多变量,其中包括:
创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。
注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。
Q3 既然有了epoll_ctl,那肯定某处会调用epoll_wait
1.1.6 InputReader的构造函数
第一个参数是EventHub对象,第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型
把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量
1.1.7 InputManager.cpp的initialize函数
用InputReader对象作为参数创建InputReaderThread线程对象,用InputDispatcher作为参数创建InputDispatcherThread线程对象,InputReaderThread的构造函数:
把InputReader对象赋给mReader变量
InputDispatcherThread的构造函数:
把InputDispatcher对象赋给mDispatcher变量
进程执行到此处,InputManagerService的初始化就完成,整个过程都是在创建、初始化各种对象。
Q4 既然创建了线程对象,那么就应该在某处启动线程,否则线程无法运行,在哪里启动线程
1.2 WindowManagerService的main方法
main方法创建了WindowManagerService对象,第二个参数是InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager
1.3 ServiceManager的addService方法
把InputManagerService服务注册到ServiceManager中
1.4 InputManagerService的setWindowManagerCallbacks方法
把InputMonitor对象传递到InputManagerService中去,方便回调,比如7.2节通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法
1.5 InputManagerService的start方法
start方法中有两句:
nativeStart的本地实现:
ptr在1.1.1节已经提到过,是NativeInputManager对象地址,只不过是长整形,此处再转化成NativeInputManager型指针
Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调
Q5 创建了看门狗,有什么用?
nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart —-> InputManager.cpp的start()
1.5.1 InputManager.cpp的start
首先启动分发器线程InputDispatcherThread,调用run方法后,开始执行threadLoop方法,如果threadLoop返回true,就再次执行threadLoop方法直到requestExit方法停止线程;再调用InputReaderThread的run方法启动接收器线程。这回答了1.1.7节Q4问题,只要创建了线程,那就应该有启动线程的地方。
threadLoop中调用了mDispatcher->dispatchOnce()
1.5.2 InputDispatcher.cpp的dispatchOnce
一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked
1.5.2.1 InputDispatcher.cpp的dispatchOnceInnerLocked
mPendingEvent初始化为空,mInboundQueue队列也为空,下面语句:
成立,进入到if语句中
如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false
mKeyRepeatState.lastKeyEntry初始化为false,跳过:
这条语句,继续:
mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回到dispatchOnce方法中继续后面语句的执行:
一开始,没有命令待处理,runCommandsLockedInterruptible返回false,跳过语句;如果有命令的话,就取出来处理
1.5.2.2 Looper的pollOnce
看到熟悉的语句了,pollOnce一直执行到epoll_wait监控管道读取端,如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;如果还没有事件,进程InputDispatcherThread在此等待,这就回答了Q3
Q6 既然有Looper的pollOnce方法,就有对应的唤醒方法,在哪里?
1.5.3 InputReader.cpp的loopOnce
InputDeviceInfo:描述输入设备特征和特性的类
getEvents用来获取输入设备信息、输入事件;processEventsLocked用来处理获得的事件的;按照顺序,首先进入到getEvents
1.5.3.1-1 EventHub.cpp的getEvents函数
第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件
第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:
time:事件的执行时间
type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等
code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)
value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值
mNeedToReopenDevices,mClosingDevices一开始初始化为false,跳过:
与
mNeedToScanDevices初始化为true,执行到:
mNeedToScanDevices初始化为true,scanDevicesLocked的调用过程:scanDevicesLocked() —-> scanDirLocked(DEVICE_PATH) —-> openDeviceLocked(devname)
DEVICE_PATH被赋值为/dev/input,这是输入设备节点目录,该目录下包含所有输入设备节点,在命令行下进入该目录看看,一般包含:
event0一般代表按键或键盘,可用命令cat /proc/bus/input/device查看所有输入设备详细信息
1.5.3.2 EventHub.cpp的openDeviceLocked函数
devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0
open以读写方式打开该设备文件,返回设备文件描述符fd
然后做了一系列动作:获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。
从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象
检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,表示当前发生输入事件的设备是按键或键盘;
在这段代码之后,系统做了很多事情,比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;如果是键盘、操纵杆等,就配置其布局。这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。
把设备文件描述符注册到epoll对象中,如果linux内核版本号大于或等于3.5,就设置监控事件为EPOLLIN,表示可读事件,并把设备id保存到data结构体中。
最后调用addDeviceLocked(device)把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices,mOpeningDevices初始化为0,表示下一个设备还没有,同时设mOpeningDevices为当前设备对象。
openDeviceLocked函数执行完毕返回到getEvents的scanDevicesLocked()函数后继续执行
1.5.3.1-2 EventHub.cpp的getEvents函数
mOpeningDevices不为空,进入到while循环:
该while循环把所有设备Device的内容保存到RawEvent结构体中,把event->type设为DEVICE_ADDED,意味着事件被成功添加到RawEvent,再把mNeedToSendFinishedDeviceScan设为true,意味着结束设备扫描。继续执行到:
mPendingEventCount和mPendingEventIndex都初始化为0,while循环不成立,跳过该while循环
mPendingINotify初始化为false,跳过:
deviceChanged也为false,跳过:
执行到:
不相等,break跳出for循环执行到最后event-buffer得到输入设备的个数,返回到loopOnce中执行processEventsLocked —> addDeviceLocked把设备添加到KeyedVector中,同时设置一些变量,比如把keyboardSource为AINPUT_SOURCE_KEYBOARD,表示按键
因为InputReaderThread是循环执行的,进程又一次进入到getEvents函数中,开始执行for循环,这时候设备都已经取出来了就不再执行scanDevicesLocked()扫描获取设备
mPendingEventCount还是为0,所以:
仍然不成立,进程执行到:
一开始,队列等待中的事件都为0,mPendingEventIndex设为0;调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,InputRead线程处于等待状态,等待被唤醒,什么时候被唤醒
在1.1.3节,提到Q1和Q2两个问题,Q1在此处可以解答。
要想回答Q2,就必须要清楚epoll兴趣列表中注册了哪些文件描述符,在1.1.3节可知,注册了mINotifyFd, mWakeReadPipeFd和mWakeWritePipeFd;由1.5.3.2可知,注册了设备文件描述符fd。要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。搜索一下代码,发现EventHub.cpp的wake()方法中有管道写入端描述符mWakeWritePipeFd,这是一个线索:
1.5.3.3 EventHub.cpp的wake()
在wake方法中,管道写入端mWakeWritePipeFd写入了一个字符w,管道中有数据了,epoll机制监控到管道有I/O事件发生,于是epoll_wait执行,返回发生I/O事件的个数,并且把发生的事件放在epoll_event结构数组mPendingEventItems中,这时调用者线程InputReaderThread被唤醒,这就回答了1.1.3节Q2问题,但是新的问题又来了
Q7:wake方法是什么被调用的?被谁调用的?
先继续epoll_wait方法后面的代码分析
1.5.3.1-3 EventHub.cpp的getEvents函数
epoll_wait返回发生事件的个数赋给pollResult,pollResult肯定大于0,执行:
pollResult赋给了mPendingEventCount,表明有事件待处理
再次执行getEvents中的for循环
mOpeningDevices在1.5.3.1-1节中已经被赋为device->next即0,所以while循环:
不成立,跳过。
mPendingEventCount大于0了,mPendingEventIndex < mPendingEventCount成立,进入到下面while循环:
根据发生事件的类型,判断:
成立,进程执行到:
从管道中读取数据,该数据就是一个字符w,执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;然后1.5.3.3节的wake()函数又调用write写入字符w触发了epoll_wait,唤醒了InputReader线程,然后又开始执行for循环……
Q8 这样循环下去的意义何在?
循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句:
成立,跳出for循环返回了,如果此时有事件发生的话,因为epoll_wait还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。
1.5.3.1-4 EventHub.cpp的getEvents函数
在1.5.3.2节openDeviceLocked函数中,把输入设备文件描述符注册到mEpollFd中,因此,当有输入事件发生时,epoll_wait方法开始执行返回发生的个数赋给pollResult变量,此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环,进入到:
此时eventItem.data.u32的值是设备id,if语句:
与
都不成立,进程执行到:
根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中,如果没出错的话,执行:
计算出有多少个事件并取出来保存到input_event结构体变量iev中,然后计算相应的事件执行时间,再把input_event结构体变量中的内容保存到原始事件RawEvent中,然后循环直到所有事件都被获取到后,进程执行:
event指向的内存现在已经保存了事件,而buffer还是指向首地址,不相等,if成立,执行break跳出for(::)无限循环,执行到getEvents最后一句:
返回读取事件的个数,并返回到InputReader的loopOnce中这一句:
count大于0,执行:
调用processEventsLocked对事件进行处理,此处属于事件处理阶段,在第2节分析。
第1节做了3件事:a. 创建InputDispatchThread, InputReaderThread,InputManager等主要输入系统核心组件
b. 扫描所有输入设备,获取输入设备各种信息
c. 获取输入设备事件
第1节Framework层如何获取事件的过程分析完毕,还留下Q6问题
通过getEvents获得事件后,执行过程:processEventsLocked —-> processEventsForDeviceLocked
在processEventsLocked中,rawEvent->type表示事件的类型,有如下几种:
本文针对按键事件,因此type是EV_KEY,小于FIRST_SYNTHETIC_EVENT,
2.1 InputReader.cpp的processEventsForDeviceLocked函数
从KeyedVector容器中根据设备id号取出设备对象赋给device,参数count为多少?正常情况下按键按下一次,产生一个按下事件,类型为EV_KEY,还有一个同步事件EV_SYNC,因此count为2
然后调用过程为:
device->process(rawEvents, count) —-> mapper->process(rawEvent) —-> KeyboardInputMapper::processKey
不同的输入时间对应不同的InputMapper对象,按键事件对应的是KeyboardInputMapper对象,就执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。
再根据按键类型,执行到KeyboardInputMapper::processKey函数,在processKey中,主要代码在最后部分:
NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:
nsecs_t eventTime:按键事件的发生时间
int32_t deviceId:按键的设备id
uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘
uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。
int32_t action:按键动作标志,是按下按键还是松开按键
int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM
int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置
int32_t scanCode:原始按键值,就是从底层获取到的按键值
int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0
nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量
mQueuedListener是在1.1.6节创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数,如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。
2.2 InputDispatcher.cpp的notifyKey函数
该函数做了很重要的任务:前半部分对传递过来的按键事件进行检查、验证,之后处理特殊按键,再把按键放到InboundQueue队列中,最后调用Looper对象的wake像管道中写入字符唤醒InputDispatcherThread线程
检查按键是否按下、松开
设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件,比如手机上除了屏幕显示外的MENU、HOME、BACK键
设置metaState为meta键标志
设置该标志表示按键事件是可信任的,比如,一个直接与Android机器相连接的按键设备
把参数传递给KeyEvent对象event
拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等,这一步将在第7节分析
创建KeyEntry对象并把按键事件作为该对象的内容,enqueueInboundEventLocked函数根据输入事件的类型作进一步处理:
mInboundQueue一开始没有数据为空,enqueueAtTail函数把按键事件对象KeyEntry插入到mInboundQueue队尾。
case语句用来切换app,如果是HOME、挂号按键,就把needWake设为true,表示可以唤醒InputDispatcherThread来分发按键事件,否则,不唤醒InputDispatcherThread,那么按键就不会处理
wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程是谁?在1.5.2.2节,InputDispatcherThread线程执行到了Looper对象的pollOnce函数时处于阻塞状态,此时epoll机制检测到管道中有数据写入事件发生,由这篇文章:Handler机制可知,读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread,Q6也就有了答案。
小结:
把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。
现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。
由2.2节可知,InputDispatcherThread线程被唤醒后,又一次地执行dispatchOnce函数,在dispatchOnce中调用了dispatchOnceInnerLocked函数
3.1 InputDispatcher.cpp的dispatchOnceInnerLocked
mInboundQueue中已经有了按键事件,不为空,执行到这句:
dequeueAtHead函数从队列mInboundQueue头部取出一个元素EventEntry对象交给mPendingEvent,EventEntry代表按键事件
在switch语句中,根据类型来选择执行语句,按键为TYPE_KEY
忽略不重要的代码,下一步调用dispatchKeyLocked函数:
先判断是否包含重复按键事件,如果是,暂时先清除掉,然后计算repeatCount值;如果没有重复按键事件,就保存按键状态、执行时间;如果计算后的repeatCount为1,设flags为AKEY_EVENT_FLAG_LONG_PRESS,表示长按事件。
把dispatchInProgress设为true,意味着即将进行分发事件了
entry->interceptKeyResult一开始默认为INTERCEPT_KEY_RESULT_UNKNOWN,if语句成立
如果entry->policyFlags为POLICY_FLAG_PASS_TO_USER,表示按键需要传递给应用程序;doInterceptKeyBeforeDispatchingLockedInterruptible函数也是拦截按键处理的方法,在7.2节详解
InputTarget结构体:指定一个输入事件如何被分发到一个特定窗口。该结构体包含了很多特征变量:x,y坐标,输入事件通道InputChannel等;findFocusedWindowTargetsLocked找到获得焦点的窗口,如果成功,返回值设为INPUT_EVENT_INJECTION_SUCCEEDED,并把获得焦点的窗口的输入通道保存到InputTarget对象中,再把InputTarget对象放到inputTargets堆栈顶,setInjectionResultLocked函数把该获得焦点的标志INPUT_EVENT_INJECTION_SUCCEEDED保存到InjectionState结构体元素injectionResult中
dispatchEventLocked函数从inputTarget容器中取出获得焦点的输入通道管理对象Connection,Connection用来管理单个输入通道InputChannel的分发状态。dispatchEventLocked函数的调用过程:
dispatchEventLocked —-> prepareDispatchCycleLocked —-> enqueueDispatchEntriesLocked
outboundQueue一开始为空,当执行完enqueueDispatchEntryLocked后,outboundQueue就有了按键事件DispatchEntry,不为空,所以if语句:
成立,startDispatchCycleLocked开始发送事件,先看enqueueDispatchEntryLocked:
enqueueDispatchEntryLocked函数相关代码:
最后一个参数dispatchMode针对触摸的,与触摸相关的代码暂时去掉
创建DispatchEntry按键事件对象,把该对象作为一个发送数据包加入到outboundQueue队列中
trackKey函数把按键分为按下和松开两类事件处理,如果是按下就就先保存到KeyMemento结构体中,如果是在松开状态下,该事件是回退事件,即不在应用程序中处理的事件,就从回退事件集合中删除该按键
再把新构建的按键事件数据包添加到outboundQueue队列尾部即栈顶。
startDispatchCycleLocked函数:
当outboundQueue非空并且其他条件都具备时(比如,inputchannel已经注册,通信没有错误等),取出outboundQueue队列头元素,赋给dispatchEntry,再取出事件对象KeyEntry,根据事件类型确定case语句分支,如果是按键事件,就调用connection的InputPublisher的publishKeyEvent函数发送到inputchannel中,如果publishKeyEvent返回0,表示按键事件发送成功
3.2 InputTransport.cpp的publishKeyEvent
创建Message消息,把传递过来的事件信息保存到Message对象中,调用服务端管道InputChannel的sendMessage方法发送消息,sendMessage源码:
send系统调用相当于write调用,区别在于:send专用于socket文件描述符;send多了一个flags参数表示I/O事件的行为。send向服务端fd发送消息msg,如果成功,返回成功发送消息的大小;返回-1表示发送了错误
在3.4.1.1节通过socketpair创建一对socket对象,一个用作服务端,一个用作客户端,并且是已经连接好的插口,无需再通过bind、connect去连接,两端都可以发送、接收,全双工通信。在按键事件中,用socket[0]作为服务端,通过send向服务端发送数据,recv从客户端socket[1]接收数据。
在3.5.3.1节,socket客户端文件描述符被注册到了主线程epoll兴趣列表中,如果socket服务端没有往inputchannel中写入数据,客户端暂时处于阻塞状态,一旦服务端有了数据后,客户端也被唤醒了,客户端唤醒后的处理过程在第5节分析。
Q9 在publishKeyEvent中调用这句时:
mChannel是服务端管道对象,该对象如何创建、注册的?socket文件描述符mFd什么时候创建;既然有服务端,肯定有客户端来接收数据,那么应用程序客户端socket如何创建?应用程序客户端InputChannel对象又是什么时候创建、注册的?
关于此问题将在第4节分析
涉及到应用程序相关的,就从ViewRootImpl来分析
4.1 ViewRootImpl.java的setView方法
当窗口没有按键事件传输通道时,创建一个InputChannel对象,相当于一个管道,其他进程通过该管道像窗口发送按键事件消息。
mWindowSession在ViewRootImpl构造器中创建:
4.2 WindowManagerGlobal.java的getWindowSession方法
getWindowManagerService方法源码:
getWindowSession方法做了很多工作,单例类InputMethodManager通过getInstance返回其唯一的对象,InputMethodManager表示输入法框架系统级API,用以输入法和应用程序之间的交互。
getService(“window”)获得一个WindowManagerService服务的Binder对象,实际是BinderProxy对象,asInterface接口根据BinderProxy对象生成Proxy客户端代理对象,返回到getWindowSession中赋给IWindowManager接口类型windowManager变量,然后利用该代理对象来访问WindowManagerService服务端中的方法openSession,其参数涵义:
第一个参数是一个IWindowSessionCallback.Stub对象
第二个参数imm.getClient(),返回IInputMethodClient.Stub对象
第三个参数imm.getInputContext(),返回ControlledInputConnectionWrapper对象
在openSession方法中,创建了Session对象,并把这三个参数及WindowManagerService对象传递到Session中
得到Session对象后,返回到4.1节ViewRootImpl构造器中赋给IWindowSession接口类型mWindowSession,再调用Session对象的addToDisplay
4.3 Session.java的addToDisplay方法
mWindow:W对象,W实现了IWindow.Stub抽象类,继承了Binder类
mSeq:一个整型,每个按键事件的序号
mWindowAttributes:初始化语句有:
创建了一个布局参数对象
mAppVisible:确定mView即DecorView的可见性,默认为true,可见
mDisplay.getDisplayId():获得应用程序逻辑显示区域id号
mAttachInfo.mContentInsets:
一个矩形对象,作为窗口可见的矩形修饰
mAttachInfo.mStableInsets:
也是一个矩形对象,作为窗口固定的修饰
mInputChannel:待注册的管道对象
mService是WindowManagerService对象,在addToDisplay中调用WindowManagerService的addWindow方法
4.4 WindowManagerService.java的addWindow方法
与inputChannel相关部分:
4.4.1 InputChannel的openInputChannelPair
openInputChannelPair的实现在本地方法android_view_InputChannel_nativeOpenInputChannelPair中:
声明服务端管道serverChannel、客户端管道clientChannel,作为openInputChannelPair的参数
4.4.1.1 InputTransprot.cpp的openInputChannelPair
创建一个饱含2个元素的socket数组,socketpair创建一对socket对象,SOCK_SEQPACKET表示创建连续可靠的数据包连接,如果创建成功,返回0,如果返回-1,出错。
以这一对socket文件描述符、通道名称作为参数分别创建服务端管道对象outServerChannel和客户端管道对象clientChannelName
在InputChannel构造函数中,把管道名称传给mName,socket文件描述符保存到mFd中,设置mFd为非阻塞模式
4.4.1.2 android_view_InputChannel.cpp的android_view_InputChannel_nativeOpenInputChannelPair
创建包含2个元素的数组,数组对象类型是gInputChannelClassInfo.clazz即InputChannel类型,元素初始化为空
用已创建的服务端通道serverChannel和客户端通道clientChannel作为参数分别创建本地通道对象NativeInputChannel,android_view_InputChannel_createInputChannel:
创建Java层InputChannel类的对象保存到inputChannelObj中,如果成功,再调用android_view_InputChannel_setNativeInputChannel把刚才创建的NativeInputChannel对象指针赋给InputChannel对象中的变量Ptr,最后返回新创建的java层的InputChannel对象分别赋给serverChannelObj和clientChannelObj
用新创建的一对管道对象初始化数组channelPair,返回该数组赋给WindowManagerService中的inputChannels数组
小结:
makeInputChannelName创建了唯一的管道名称,openInputChannelPair以该名称作为参数创建一对管道InputChannel对象,存储在数组inputChannels中,inputChannels[0]作为服务端管道提供给inputdispatcher使用,用来发送按键事件;inputChannels[1]作为客户端管道提供给应用程序主线程使用,用来接收、消费按键事件。
4.4.2 InputManagerService的registerInputChannel
服务端管道inputChannels[0]创建好后,需要注册:
参数win.mInputWindowHandle是一个输入事件窗口的句柄,有多个作用:InputDispatcher在发送按键事件前会通过该句柄检查是否有获得焦点的窗口;如果有焦点的窗口,可以通过该句柄获得应用程序的InputChannel
registerInputChannel —-> nativeRegisterInputChannel
4.4.2.1 com_android_server_input_InputManagerService.cpp的nativeRegisterInputChannel函数
先获得本地InputChannel对象和InputWindowHandle对象,再调用NativeInputManager的registerInputChannel函数:
通过InputManager对象获得对应的InputDispatcher对象,进而调用其registerInputChannel函数注册
检查inputChannel是否注册,首次执行时,没有注册
通过创建的管道inputChannel以及窗口句柄inputWindowHandle创建Connection对象,Connection用来管理inputChannel
getFd获得服务端管道inputChannel上的socket文件描述符,然后以键值对的方式把connect加入到KeyedVector中,这就完成了服务端通道的注册。
然后把socket服务端加入到epoll兴趣列表中,mLooper属于InputDispatcherThread,不属于主线程,注意与4.5.3.1节的区别,4.5.3.1节是注册到主线程对应的epoll对象中。ALOOPER_EVENT_INPUT表示可读事件,意思是在服务端socket中读取数据,这样设置的目的在于,当客户端socket接受到事件处理完后会向服务端发送一个反馈信号,服务端接收此信号后会回调handleReceiveCallback函数,该函数会再一次执行startDispatchCycleLocked函数从outboundQueue中取出按键事件进行发送,循环处理。
addFd函数的具体执行过程与4.5.4节几乎一样,不再分析。
4.4.3 InputChannel.java的transferTo
在4.4.2节已经注册了服务端管道inputChannels[0],那么客户端管道inputChannels[1]如何注册?
outInputChannel是InputChannel对象,inputChannels[1]也是刚刚创建的InputChannel对象,transferTo两头都是InputChannel对象,这又是怎么回事
transferTo的调用者是当前新创建客户端管道对象,参数outInputChannel是在ViewRootImpl中创建的,两者是不同的对象。
4.4.3.1 nativeTransferTo的本地实现
env:JNI接口对象
obj:本地方法所在对象的引用,就是inputChannels[1]客户端对象的引用
otherObj:在ViewRootImpl中创建的InputChannel对象
首先调用android_view_InputChannel_getNativeInputChannel函数
传过来的对象时otherObj,其变量mPtr在初始化时为空,没有额外赋值,所以longPtr为空,这句:
不成立
又一次调用android_view_InputChannel_getNativeInputChannel函数,只不过第二个参数是obj即inputChannels[1],此时inputChannels[1]中的mPtr指针不为空,因此在3.4.1.2节中设置了mPtr为inputChannels[1]对应的NativeInputChannel地址,返回NativeInputChannel指针给nativeInputChannel变量,而后调用android_view_InputChannel_setNativeInputChannel函数把otherObj中的mPtr设为NativeInputChannel对象地址,这就表明ViewRootImpl中创建的InputChannel对象是客户端管道
读到此处可知,transferTo方法只是把ViewRootImpl中创建的InputChannel对象中的mPtr变量赋了值,该值就是客户端InputChannle对应的NativeInputChannel对象地址。
4.5 ViewRootImpl.java的setView方法
在4.4.2.1节,注册服务端管道InputChannel[0]时,创建了InputPublisher对象,用来发送消息到input channel,那么注册在客户端管道InputChannel[1]时,也会创建一个接收消息的对象,这就是InputConsumer,从管道中获取事件。客户端如何创建?还是在ViewRootImpl.java的setView方法中:
view是DecorView对象,DecorView实现了RootViewSurfaceTaker接口,因此,view也是RootViewSurfaceTaker接口类型,if语句成立,willYouTakeTheInputQueue实际返回一个NativeActivity对象,因为NativeActivity实现了InputQueue.Callback接口
InputQueue:应用程序通过InputQueue来获得输入设备事件
InputQueue.Callback:当InputQueue绑定、解除绑定与线程关系时的回调接口
创建了InputQueue对象,作为NativeActivity中回调方法onInputQueueCreated的参数;用InputChannel和Looper对象作为参数创建WindowInputEventReceiver对象并用mInputEventReceiver指向该对象。
InputQueue构造器中调用了本地方法nativeInit
第一个参数是InputQueue弱引用对象,第二个参数是主线程MessageQueue对象
4.5.1 android_view_InputQueue.cpp的nativeInit
android_os_MessageQueue_getMessageQueue获得C++层NativeMessageQueue对象,也是MessageQueue对象,getLooper()获得Looper对象,queueWeak是java层传递过来的InputQueue弱引用对象,createQueue源码:
创建了一个管道pipeFds,设置管道读、写端为非阻塞模式
创建了c++层InputQueue对象,赋给queue变量。InputQueue的构造函数:
分别把管道读端、写端文件描述符赋给mDispatchReadFd、mDispatchWriteFd;用JNI接口方法NewGlobalRef创建inputQueueObj本地全局引用赋给mInputQueueWeakGlobal
4.5.2 NativeActivity的onInputQueueCreated
把创建的InputQueue对象赋给mCurInputQueue,当创建好InputQueue后,既可回调onInputQueueCreated重写自己的代码,表明可以从InputQueue中获取事件
4.5.3 WindowInputEventReceiver的构造器
调用WindowInputEventReceiver的构造方法创建了一个WindowInputEventReceiver对象,其构造方法没有实现,直接调用父类InputEventReceiver的构造方法,第一个参数就是客户端管道对象,其内部mPtr变量指向了客户端NativeInputChannel对象地址。
第一个参数this,实际类型是WindowInputEventReceiver类型,这个参数很重要,在5.3节会根据该类型决定调用哪个onInputEvent方法。
用本地InputChannel, NativeMessageQueue及java层InputEventReceiver对象作为参数创建本地NativeInputEventReceiver对象并初始化,receiverWeak就是传递过来的WindowInputEventReceiver对象,赋给了mReceiverWeakGlobal变量。
创建好NativeInputEventReceiver对象后返回到java层InputEventReceiver中赋给mReceiverPtr变量,java层拥有这个变量就等于拥有了c++层NativeInputEventReceiver对象的指针
4.5.3.1 NativeInputEventReceiver的initialize函数
InputConsumer获取客户端inputChannel管道上的socket文件描述符添加到epoll对象兴趣列表中,fd是socket的客户端。mMessageQueue是主线程中的消息队列,getLooper返回主线程Looper对象,因此,客户端socket文件描述符被注册到了主线程中epoll对象中。
4.5.4 Looper.cpp的addFd函数
addFd中第2个参数为0,第3个参数是ALOOPER_EVENT_INPUT,第4个参数this,是NativeInputEventReceiver对象,也是LooperCallback对象类型,第5个参数是NULL
第一个if语句不成立,把ident设置成POLL_CALLBACK,表示调用者有回调方法会被执行。
设置监控事件为EPOLLIN可读事件
用传递过来的参数初始化Request结构体变量request
声明epoll机制事件结构体变量eventItem并初始化,设events为可读事件,注册的文件描述符为socket服务端fd
判断容器mRequests中是否有socket文件描述符fd,如果没有,就把fd注册到epoll对象mEpollFd中,否则,修改现有的epoll对象的兴趣列表中的fd,更新了eventItem。同时把调用者相关信息对象Request与fd作为一对映射关系保存到mRequests容器中,待执行epoll_wait时使用。
小结:
应用程序端管道inputChannels[1]注册在InputEventReceiver中,其socket对象注册到主线程epoll对象兴趣列表中,当socket对端即服务端inputChannels[0]上有数据写入后,应用程序便可从服务端socket对象上读取数据。
android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层,内核层传递过程不在本文研究范围内,本文主要对按键事件在Framework层、应用层的传递过程进行分析(本文基于Android5.1.1版本),带着问题出发:
1. Framework层如何获得输入事件
2. Framework层获得按键事件后如何处理、存储
3. Framework层如何分发、传递给应用层
4. Framework层服务端管道和应用程序客户端管道如何创建、注册
5. 应用层如何从Framework层接收按键事件
6. 应用层接收到按键事件后如何传递
7. 特殊按键如何处理
8. 总结
1. Framework层如何获得输入事件
要分析输入系统,最好先从输入事件系统服务InputManagerService入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。1.1 InputManagerService的初始化
Android系统启动时,Android部分第一个用户空间进程zygote(注:如果从Linux角度出发,第一个用户空间进程是init)首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerService、PowerManagerService、PackageManagerService、InputManagerService等,相关代码在SystemServer.Java的run方法中,与InputManagerService相关部分:
1) inputManager = new InputManagerService(context); 2) wm = WindowManagerService.main 4000 (context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); 3) ServiceManager.addService(Context.INPUT_SERVICE, inputManager); 4) inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); 5) inputManager.start();
1.1.1 InputManagerService的构造方法
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
用Looper中的消息队列作为参数,调用本地方法nativeInit,返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。
LocalServices.addService(InputManagerInternal.class, new LocalService());
把InputManagerInternal.class和LocalService对象作为一对映射添加到ArrayMap<Class<?>, Object>中
1.1.2 nativeInit方法实现
static jlongnativeInit(JNIEnv* env, jclassclazz, jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im); }
android_os_MessageQueue_getMessageQueue方法:
sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) { jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr); return reinterpret_cast<NativeMessageQueue*>(ptr); }
获得了C++层NativeMessageQueue对象,NativeMessageQueue对象是在创建Looper对象时创建的,在本博客Handler机制中已经分析过
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im);
创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;getLooper()获得c++层的Looper对象,在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中
NativeInputManager的构造函数:
NativeInputManager::NativeInputManager(jobjectcontextObj, jobjectserviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { AutoMutex_l(mLock); mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法;创建c++层中全局变量mContextObj, mServiceObj;创建EventHub对象并作为参数创建InputManager对象。
1.1.3 EventHub.cpp的构造函数
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); mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d", DEVICE_PATH, errno); struct epoll_eventeventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = EPOLL_ID_INOTIFY; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2]; result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", errno); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); eventItem.data.u32 = EPOLL_ID_WAKE; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); int major, minor; getLinuxRelease(&major, &minor); // EPOLLWAKEUP was introduced in kernel 3.5 mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5); }
初始化必要的变量,采用epoll机制系统调用接口epoll_create创建epoll对象,返回文件描述符mEpollFd,代表epoll对象;采用inotify机制监听文件或目录的移动、读取、写入或删除等事件,inotify机制主要包含2个系统调用接口:inotify_init, inotify_add_watch
inotify_init:创建一个inotify对象,如果成功,返回一个文件描述符,作为该对象;返回-1表示出错
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)
inotify_add_watch:把监控项添加到mINotifyFd对象的监控列表中,第二个参数DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;第三个参数是一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。IN_DELETE:当被监控目录内删除文件或目录时触发该事件;IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。
如果inotify_add_watch执行成功,返回一个非负监控描述符(假如为wd),代表被监控的项,如果将来不需要再监听了,可以使用inotify_rm_watch(fd, wd)删除所添加的监控项wd。
struct epoll_eventeventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = EPOLL_ID_INOTIFY; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
把inotify对象mINotifyFd添加到epoll对象的兴趣列表中,此处采用inotify与epoll机制结合起来检查文件
eventItem.data.u32 = EPOLL_ID_WAKE; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
创建了管道对象读取端文件描述符mWakeReadPipeFd并添加到epoll对象的兴趣列表中
读到此处,提出2个问题:
Q1 epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait,猜测在代码某处肯定有epoll_wait,否则epoll机制无法工作
Q2 如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用
记住这两个问题,有助于在分析多进程控制时的代码走向
1.1.4 InputManager.cpp的构造器
mInputManager = new InputManager(eventHub, this, this);
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }
第一个参数是刚创建的EventHub对象,第二、三个参数都是this,当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager
用这两个传递过去的对象类型作为参数分别创建了InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象
1.1.5 InputDispatcher的构造函数
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy), mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(NULL), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mKeyRepeatState.lastKeyEntry = NULL; policy->getDispatcherConfiguration(&mConfig); }
把传递过来的NativeInputManager对象赋给mPolicy,该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型,然后又初始化了很多变量,其中包括:
mLooper = new Looper(false);
创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。
注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。
Q3 既然有了epoll_ctl,那肯定某处会调用epoll_wait
1.1.6 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); { // acquire lock AutoMutex_l(mLock); refreshConfigurationLocked(0); updateGlobalMetaStateLocked(); } // release lock }
第一个参数是EventHub对象,第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型
mQueuedListener = new QueuedInputListener(listener);
把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量
1.1.7 InputManager.cpp的initialize函数
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
用InputReader对象作为参数创建InputReaderThread线程对象,用InputDispatcher作为参数创建InputDispatcherThread线程对象,InputReaderThread的构造函数:
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : Thread(/*canCallJava*/ true), mReader(reader) { }
把InputReader对象赋给mReader变量
InputDispatcherThread的构造函数:
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { }
把InputDispatcher对象赋给mDispatcher变量
进程执行到此处,InputManagerService的初始化就完成,整个过程都是在创建、初始化各种对象。
Q4 既然创建了线程对象,那么就应该在某处启动线程,否则线程无法运行,在哪里启动线程
1.2 WindowManagerService的main方法
wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore);
public static WindowManagerServicemain(final Contextcontext, final InputManagerServiceim, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; DisplayThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; }
mInputManager = inputManager;
main方法创建了WindowManagerService对象,第二个参数是InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager
1.3 ServiceManager的addService方法
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
把InputManagerService服务注册到ServiceManager中
1.4 InputManagerService的setWindowManagerCallbacks方法
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
把InputMonitor对象传递到InputManagerService中去,方便回调,比如7.2节通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法
1.5 InputManagerService的start方法
start方法中有两句:
nativeStart(mPtr); Watchdog.getInstance().addMonitor(this);
nativeStart的本地实现:
static void nativeStart(JNIEnv* env, jclassclazz, jlongptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_tresult = im->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } }
ptr在1.1.1节已经提到过,是NativeInputManager对象地址,只不过是长整形,此处再转化成NativeInputManager型指针
Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调
Q5 创建了看门狗,有什么用?
nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart —-> InputManager.cpp的start()
1.5.1 InputManager.cpp的start
status_tInputManager::start() { status_tresult = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputReader thread due to error %d.", result); mDispatcherThread->requestExit(); return result; } return OK; }
首先启动分发器线程InputDispatcherThread,调用run方法后,开始执行threadLoop方法,如果threadLoop返回true,就再次执行threadLoop方法直到requestExit方法停止线程;再调用InputReaderThread的run方法启动接收器线程。这回答了1.1.7节Q4问题,只要创建了线程,那就应该有启动线程的地方。
threadLoop中调用了mDispatcher->dispatchOnce()
1.5.2 InputDispatcher.cpp的dispatchOnce
void InputDispatcher::dispatchOnce() { nsecs_tnextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex_l(mLock); mDispatcherIsAliveCondition.broadcast(); // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_tcurrentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis); }
if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); }
一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked
1.5.2.1 InputDispatcher.cpp的dispatchOnceInnerLocked
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; } // Synthesize a key repeat if appropriate. if (mKeyRepeatState.lastKeyEntry) { if (currentTime >= mKeyRepeatState.nextRepeatTime) { mPendingEvent = synthesizeKeyRepeatLocked(currentTime); } else { if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { *nextWakeupTime = mKeyRepeatState.nextRepeatTime; } } } // Nothing to do if there is no pending event. if (!mPendingEvent) { return; } } else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); traceInboundQueueLengthLocked(); } // Poke user activity for this event. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(mPendingEvent); } // Get ready to dispatch the event. resetANRTimeoutsLocked(); }
mPendingEvent初始化为空,mInboundQueue队列也为空,下面语句:
if (! mPendingEvent) { if (mInboundQueue.isEmpty()) {
成立,进入到if语句中
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; }
如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false
mKeyRepeatState.lastKeyEntry初始化为false,跳过:
if (mKeyRepeatState.lastKeyEntry) {
这条语句,继续:
if (!mPendingEvent) { return; }
mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回到dispatchOnce方法中继续后面语句的执行:
if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; }
一开始,没有命令待处理,runCommandsLockedInterruptible返回false,跳过语句;如果有命令的话,就取出来处理
1.5.2.2 Looper的pollOnce
mLooper->pollOnce(timeoutMillis);
看到熟悉的语句了,pollOnce一直执行到epoll_wait监控管道读取端,如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;如果还没有事件,进程InputDispatcherThread在此等待,这就回答了Q3
Q6 既然有Looper的pollOnce方法,就有对应的唤醒方法,在哪里?
1.5.3 InputReader.cpp的loopOnce
void InputReader::loopOnce() { int32_toldGeneration; int32_ttimeoutMillis; bool inputDevicesChanged = false; Vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex_l(mLock); oldGeneration = mGeneration; timeoutMillis = -1; uint32_tchanges = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; timeoutMillis = 0; refreshConfigurationLocked(changes); } else if (mNextTimeout != LLONG_MAX) { nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex_l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } if (mNextTimeout != LLONG_MAX) { nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { #if DEBUG_RAW_EVENTS ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); #endif mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } if (oldGeneration != mGeneration) { inputDevicesChanged = true; getInputDevicesLocked(inputDevices); } } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked // on another thread similarly waiting to acquire the InputReader lock thereby // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. mQueuedListener->flush(); }
InputDeviceInfo:描述输入设备特征和特性的类
getEvents用来获取输入设备信息、输入事件;processEventsLocked用来处理获得的事件的;按照顺序,首先进入到getEvents
1.5.3.1-1 EventHub.cpp的getEvents函数
size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_tbufferSize) { ALOG_ASSERT(bufferSize >= 1); AutoMutex_l(mLock); struct input_eventreadBuffer[bufferSize]; RawEvent* event = buffer; size_tcapacity = bufferSize; bool awoken = false; for (;;) { nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC); // Reopen input devices if needed. if (mNeedToReopenDevices) { mNeedToReopenDevices = false; ALOGI("Reopening all input devices due to a configuration change."); closeAllDevicesLocked(); mNeedToScanDevices = true; break; // return to the caller before we actually rescan } // Report any devices that had last been added/removed. while (mClosingDevices) { Device* device = mClosingDevices; ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; event->type = DEVICE_REMOVED; event += 1; deletedevice; mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; } } if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; } while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.string()); 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; } } if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; event->when = now; event->type = FINISHED_DEVICE_SCAN; event += 1; if (--capacity == 0) { break; } } // Grab the next input event. bool deviceChanged = false; while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { if (eventItem.events & EPOLLIN) { mPendingINotify = true; } else { ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; } if (eventItem.data.u32 == EPOLL_ID_WAKE) { if (eventItem.events & EPOLLIN) { ALOGV("awoken after wake()"); awoken = true; char buffer[16]; ssize_tnRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } ssize_tdeviceIndex = mDevices.indexOfKey(eventItem.data.u32); if (deviceIndex < 0) { ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.", eventItem.events, eventItem.data.u32); continue; } Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) { int32_treadSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. ALOGW("could not get event, removed? (fd: %d size: %" PRId32 " bufferSize: %zu capacity: %zu errno: %d)\n", device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { ALOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { ALOGE("could not get event (wrong size: %d)", readSize); } else { int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_tcount = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { struct input_event& iev = readBuffer[i]; ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d", device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); // Some input devices may have a better concept of the time // when an input event was actually generated than the kernel // which simply timestamps all events on entry to evdev. // This is a custom Android extension of the input protocol // mainly intended for use with uinput based device drivers. if (iev.type == EV_MSC) { if (iev.code == MSC_ANDROID_TIME_SEC) { device->timestampOverrideSec = iev.value; continue; } else if (iev.code == MSC_ANDROID_TIME_USEC) { device->timestampOverrideUsec = iev.value; continue; } } if (device->timestampOverrideSec || device->timestampOverrideUsec) { iev.time.tv_sec = device->timestampOverrideSec; iev.time.tv_usec = device->timestampOverrideUsec; if (iev.type == EV_SYN && iev.code == SYN_REPORT) { device->timestampOverrideSec = 0; device->timestampOverrideUsec = 0; } ALOGV("applied override time %d.%06d", int(iev.time.tv_sec), int(iev.time.tv_usec)); } #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of // event dispatch latency from the time the event is enqueued onto // the evdev client buffer. // // The event's timestamp fortuitously uses the same monotonic clock // time base as the rest of Android. The kernel event device driver // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call that also queries ktime_get_ts(). event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now); // Bug 7291243: Add a guard in case the kernel generates timestamps // that appear to be far into the future because they were generated // using the wrong clock source. // // This can happen because when the input device is initially opened // it has a default clock source of CLOCK_REALTIME. Any input events // enqueued right after the device is opened will have timestamps // generated using CLOCK_REALTIME. We later set the clock source // to CLOCK_MONOTONIC but it is already too late. // // Invalid input event timestamps can result in ANRs, crashes and // and other issues that are hard to track down. We must not let them // propagate through the system. // // Log a warning so that we notice the problem and recover gracefully. if (event->when >= now + 10 * 1000000000LL) { // Double-check. Time may have moved on. nsecs_ttime = systemTime(SYSTEM_TIME_MONOTONIC); if (event->when > time) { ALOGW("An input event from %s has a timestamp that appears to " "have been generated using the wrong clock source " "(expected CLOCK_MONOTONIC): " "event time %" PRId64 ", current time %" PRId64 ", call time %" PRId64 ". " "Using current time instead.", device->path.string(), event->when, time, now); event->when = time; } else { ALOGV("Event time is ok but failed the fast path and required " "an extra call to systemTime: " "event time %" PRId64 ", current time %" PRId64 12e3b ", call time %" PRId64 ".", event->when, time, now); } } #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; event->value = iev.value; event += 1; capacity -= 1; } if (capacity == 0) { // The result buffer is full. Reset the pending event index // so we will try to read the device again on the next iteration. mPendingEventIndex -= 1; break; } } } else if (eventItem.events & EPOLLHUP) { ALOGI("Removing device %s due to epoll hang-up event.", device->identifier.name.string()); deviceChanged = true; closeDeviceLocked(device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); } } // readNotify() will modify the list of devices so this must be done after // processing all other events to ensure that we read all remaining events // before closing the devices. if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; } // Report added or removed devices immediately. if (deviceChanged) { continue; } // Return now if we have collected any events or if we were explicitly awoken. if (event != buffer || awoken) { break; } // Poll for events. Mind the wake lock dance! // We hold a wake lock at all times except during epoll_wait(). This works due to some // subtle choreography. When a device driver has pending (unread) events, it acquires // a kernel wake lock. However, once the last pending event has been read, the device // driver will release the kernel wake lock. To prevent the system from going to sleep // when this happens, the EventHub holds onto its own user wake lock while the client // is processing events. Thus the system can only sleep if there are no events // pending or currently being processed. // // The timeout is advisory only. If the device is asleep, it will not wake just to // service the timeout. mPendingEventIndex = 0; mLock.unlock(); // release lock before poll, must be before release_wake_lock release_wake_lock(WAKE_LOCK_ID); int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock if (pollResult == 0) { // Timed out. mPendingEventCount = 0; break; } if (pollResult < 0) { // An error occurred. mPendingEventCount = 0; // Sleep after errors to avoid locking up the system. // Hopefully the error is transient. if (errno != EINTR) { ALOGW("poll failed (errno=%d)\n", errno); usleep(100000); } } else { // Some events occurred. mPendingEventCount = size_t(pollResult); } } // All done, return the number of events we read. return event - buffer; }
第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件
第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:
struct input_event { struct timevaltime; __u16type; __u16code; __s32value; };
time:事件的执行时间
type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等
code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)
value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值
mNeedToReopenDevices,mClosingDevices一开始初始化为false,跳过:
if (mNeedToReopenDevices) {
与
while (mClosingDevices) {
mNeedToScanDevices初始化为true,执行到:
if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; }
mNeedToScanDevices初始化为true,scanDevicesLocked的调用过程:scanDevicesLocked() —-> scanDirLocked(DEVICE_PATH) —-> openDeviceLocked(devname)
DEVICE_PATH被赋值为/dev/input,这是输入设备节点目录,该目录下包含所有输入设备节点,在命令行下进入该目录看看,一般包含:
crw-rw---- root input 13, 64 1970-01-01 08:00 event0 crw-rw---- root input 13, 65 1970-01-01 08:00 event1 crw-rw---- root input 13, 66 1970-01-01 08:00 event2 crw-rw---- root input 13, 67 1970-01-01 08:00 event3 crw-rw---- root input 13, 68 1970-01-01 08:00 event4 crw-rw---- root input 13, 69 1970-01-01 08:00 event5 crw-rw---- root input 13, 70 1970-01-01 08:00 event6 crw-rw---- root input 13, 71 1970-01-01 08:00 event7
event0一般代表按键或键盘,可用命令cat /proc/bus/input/device查看所有输入设备详细信息
1.5.3.2 EventHub.cpp的openDeviceLocked函数
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0
open以读写方式打开该设备文件,返回设备文件描述符fd
然后做了一系列动作:获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象
// See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1)); bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; }
检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,表示当前发生输入事件的设备是按键或键盘;
在这段代码之后,系统做了很多事情,比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;如果是键盘、操纵杆等,就配置其布局。这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。
// Register with epoll. struct epoll_eventeventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = mUsingEpollWakeup ? EPOLLIN : EPOLLIN | EPOLLWAKEUP; eventItem.data.u32 = deviceId; if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { ALOGE("Could not add device fd to epoll instance. errno=%d", errno); deletedevice; return -1; }
把设备文件描述符注册到epoll对象中,如果linux内核版本号大于或等于3.5,就设置监控事件为EPOLLIN,表示可读事件,并把设备id保存到data结构体中。
addDeviceLocked(device);
void EventHub::addDeviceLocked(Device* device) { mDevices.add(device->id, device); device->next = mOpeningDevices; mOpeningDevices = device; }
最后调用addDeviceLocked(device)把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices,mOpeningDevices初始化为0,表示下一个设备还没有,同时设mOpeningDevices为当前设备对象。
openDeviceLocked函数执行完毕返回到getEvents的scanDevicesLocked()函数后继续执行
1.5.3.1-2 EventHub.cpp的getEvents函数
mOpeningDevices不为空,进入到while循环:
while (mOpeningDevices != NULL) {
该while循环把所有设备Device的内容保存到RawEvent结构体中,把event->type设为DEVICE_ADDED,意味着事件被成功添加到RawEvent,再把mNeedToSendFinishedDeviceScan设为true,意味着结束设备扫描。继续执行到:
while (mPendingEventIndex < mPendingEventCount) {
mPendingEventCount和mPendingEventIndex都初始化为0,while循环不成立,跳过该while循环
mPendingINotify初始化为false,跳过:
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
deviceChanged也为false,跳过:
if (deviceChanged) {
执行到:
if (event != buffer || awoken) { break; }
不相等,break跳出for循环执行到最后event-buffer得到输入设备的个数,返回到loopOnce中执行processEventsLocked —> addDeviceLocked把设备添加到KeyedVector中,同时设置一些变量,比如把keyboardSource为AINPUT_SOURCE_KEYBOARD,表示按键
因为InputReaderThread是循环执行的,进程又一次进入到getEvents函数中,开始执行for循环,这时候设备都已经取出来了就不再执行scanDevicesLocked()扫描获取设备
mPendingEventCount还是为0,所以:
while (mPendingEventIndex < mPendingEventCount) {
仍然不成立,进程执行到:
mPendingEventIndex = 0; int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
一开始,队列等待中的事件都为0,mPendingEventIndex设为0;调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,InputRead线程处于等待状态,等待被唤醒,什么时候被唤醒
在1.1.3节,提到Q1和Q2两个问题,Q1在此处可以解答。
要想回答Q2,就必须要清楚epoll兴趣列表中注册了哪些文件描述符,在1.1.3节可知,注册了mINotifyFd, mWakeReadPipeFd和mWakeWritePipeFd;由1.5.3.2可知,注册了设备文件描述符fd。要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。搜索一下代码,发现EventHub.cpp的wake()方法中有管道写入端描述符mWakeWritePipeFd,这是一个线索:
1.5.3.3 EventHub.cpp的wake()
void EventHub::wake() { ALOGV("wake() called"); ssize_tnWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); } while (nWrite == -1 && errno == EINTR); if (nWrite != 1 && errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } }
在wake方法中,管道写入端mWakeWritePipeFd写入了一个字符w,管道中有数据了,epoll机制监控到管道有I/O事件发生,于是epoll_wait执行,返回发生I/O事件的个数,并且把发生的事件放在epoll_event结构数组mPendingEventItems中,这时调用者线程InputReaderThread被唤醒,这就回答了1.1.3节Q2问题,但是新的问题又来了
Q7:wake方法是什么被调用的?被谁调用的?
先继续epoll_wait方法后面的代码分析
1.5.3.1-3 EventHub.cpp的getEvents函数
epoll_wait返回发生事件的个数赋给pollResult,pollResult肯定大于0,执行:
mPendingEventCount = size_t(pollResult);
pollResult赋给了mPendingEventCount,表明有事件待处理
再次执行getEvents中的for循环
mOpeningDevices在1.5.3.1-1节中已经被赋为device->next即0,所以while循环:
while (mClosingDevices) {
不成立,跳过。
mPendingEventCount大于0了,mPendingEventIndex < mPendingEventCount成立,进入到下面while循环:
while (mPendingEventIndex < mPendingEventCount) {
根据发生事件的类型,判断:
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
成立,进程执行到:
do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
从管道中读取数据,该数据就是一个字符w,执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;然后1.5.3.3节的wake()函数又调用write写入字符w触发了epoll_wait,唤醒了InputReader线程,然后又开始执行for循环……
Q8 这样循环下去的意义何在?
循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句:
if (pollResult == 0) { // Timed out. mPendingEventCount = 0; break; }
成立,跳出for循环返回了,如果此时有事件发生的话,因为epoll_wait还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。
1.5.3.1-4 EventHub.cpp的getEvents函数
在1.5.3.2节openDeviceLocked函数中,把输入设备文件描述符注册到mEpollFd中,因此,当有输入事件发生时,epoll_wait方法开始执行返回发生的个数赋给pollResult变量,此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环,进入到:
while (mPendingEventIndex < mPendingEventCount) {
此时eventItem.data.u32的值是设备id,if语句:
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
与
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
都不成立,进程执行到:
Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) { int32_treadSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中,如果没出错的话,执行:
int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_tcount = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { struct input_event& iev = readBuffer[i];
计算出有多少个事件并取出来保存到input_event结构体变量iev中,然后计算相应的事件执行时间,再把input_event结构体变量中的内容保存到原始事件RawEvent中,然后循环直到所有事件都被获取到后,进程执行:
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) { break; }
event指向的内存现在已经保存了事件,而buffer还是指向首地址,不相等,if成立,执行break跳出for(::)无限循环,执行到getEvents最后一句:
return event - buffer;
返回读取事件的个数,并返回到InputReader的loopOnce中这一句:
size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
count大于0,执行:
if (count) { processEventsLocked(mEventBuffer, count); }
调用processEventsLocked对事件进行处理,此处属于事件处理阶段,在第2节分析。
第1节做了3件事:a. 创建InputDispatchThread, InputReaderThread,InputManager等主要输入系统核心组件
b. 扫描所有输入设备,获取输入设备各种信息
c. 获取输入设备事件
第1节Framework层如何获取事件的过程分析完毕,还留下Q6问题
2. Framework层获得按键事件后如何处理、存储
通过getEvents获得事件后,执行过程:processEventsLocked —-> processEventsForDeviceLocked在processEventsLocked中,rawEvent->type表示事件的类型,有如下几种:
EV_KEY,EV_ABS,EV_REL,EV_SW,EV_LED,EV_FF,...
本文针对按键事件,因此type是EV_KEY,小于FIRST_SYNTHETIC_EVENT,
2.1 InputReader.cpp的processEventsForDeviceLocked函数
void InputReader::processEventsForDeviceLocked(int32_tdeviceId, const RawEvent* rawEvents, size_tcount) { ssize_tdeviceIndex = mDevices.indexOfKey(deviceId); if (deviceIndex < 0) { ALOGW("Discarding event for unknown deviceId %d.", deviceId); return; } InputDevice* device = mDevices.valueAt(deviceIndex); if (device->isIgnored()) { //ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; } device->process(rawEvents, count); }
从KeyedVector容器中根据设备id号取出设备对象赋给device,参数count为多少?正常情况下按键按下一次,产生一个按下事件,类型为EV_KEY,还有一个同步事件EV_SYNC,因此count为2
然后调用过程为:
device->process(rawEvents, count) —-> mapper->process(rawEvent) —-> KeyboardInputMapper::processKey
不同的输入时间对应不同的InputMapper对象,按键事件对应的是KeyboardInputMapper对象,就执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。
再根据按键类型,执行到KeyboardInputMapper::processKey函数,在processKey中,主要代码在最后部分:
int32_toldMetaState = mMetaState; int32_tnewMetaState = updateMetaState(keyCode, down, oldMetaState); bool metaStateChanged = oldMetaState != newMetaState; if (metaStateChanged) { mMetaState = newMetaState; updateLedState(false); } nsecs_tdownTime = mDownTime; // Key down on external an keyboard should wake the device. // We don't do this for internal keyboards to prevent them from waking up in your pocket. // For internal keyboards, the key layout file should specify the policy flags for // each wake key individually. // TODO: Use the input device configuration to control this behavior more finely. if (down && getDevice()->isExternal()) { policyFlags |= POLICY_FLAG_WAKE; } if (mParameters.handlesKeyRepeat) { policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } if (metaStateChanged) { getContext()->updateGlobalMetaState(); } if (down && !isMetaKey(keyCode)) { getContext()->fadePointer(); } NotifyKeyArgsargs(when, getDeviceId(), mSource, policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime); getListener()->notifyKey(&args);
NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:
nsecs_t eventTime:按键事件的发生时间
int32_t deviceId:按键的设备id
uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘
uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。
int32_t action:按键动作标志,是按下按键还是松开按键
int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM
int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置
int32_t scanCode:原始按键值,就是从底层获取到的按键值
int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0
nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量
getListener()->notifyKey(&args);
InputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); }
mQueuedListener是在1.1.6节创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数,如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。
2.2 InputDispatcher.cpp的notifyKey函数
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, " "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", args->eventTime, args->deviceId, args->source, args->policyFlags, args->action, args->flags, args->keyCode, args->scanCode, args->metaState, args->downTime); #endif if (!validateKeyEvent(args->action)) { return; } uint32_tpolicyFlags = args->policyFlags; int32_tflags = args->flags; int32_tmetaState = args->metaState; if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { policyFlags |= POLICY_FLAG_VIRTUAL; flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; } if (policyFlags & POLICY_FLAG_FUNCTION) { metaState |= AMETA_FUNCTION_ON; } policyFlags |= POLICY_FLAG_TRUSTED; int32_tkeyCode = args->keyCode; if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) { int32_tnewKeyCode = AKEYCODE_UNKNOWN; if (keyCode == AKEYCODE_DEL) { newKeyCode = AKEYCODE_BACK; } else if (keyCode == AKEYCODE_ENTER) { newKeyCode = AKEYCODE_HOME; } if (newKeyCode != AKEYCODE_UNKNOWN) { AutoMutex_l(mLock); struct KeyReplacementreplacement = {keyCode, args->deviceId}; mReplacedKeys.add(replacement, newKeyCode); keyCode = newKeyCode; metaState &= ~AMETA_META_ON; } } else if (args->action == AKEY_EVENT_ACTION_UP) { // In order to maintain a consistent stream of up and down events, check to see if the key // going up is one we've replaced in a down event and haven't yet replaced in an up event, // even if the modifier was released between the down and the up events. AutoMutex_l(mLock); struct KeyReplacementreplacement = {keyCode, args->deviceId}; ssize_tindex = mReplacedKeys.indexOfKey(replacement); if (index >= 0) { keyCode = mReplacedKeys.valueAt(index); mReplacedKeys.removeItemsAt(index); metaState &= ~AMETA_META_ON; } } KeyEventevent; event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime); mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); bool needWake; { // acquire lock mLock.lock(); if (shouldSendKeyToInputFilterLocked(args)) { mLock.unlock(); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { return; // event was consumed by the filter } mLock.lock(); } int32_trepeatCount = 0; KeyEntry* newEntry = new KeyEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); } // release lock if (needWake) { mLooper->wake(); } }
该函数做了很重要的任务:前半部分对传递过来的按键事件进行检查、验证,之后处理特殊按键,再把按键放到InboundQueue队列中,最后调用Looper对象的wake像管道中写入字符唤醒InputDispatcherThread线程
if (!validateKeyEvent(args->action)) { return; }
检查按键是否按下、松开
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { policyFlags |= POLICY_FLAG_VIRTUAL; flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; }
设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件,比如手机上除了屏幕显示外的MENU、HOME、BACK键
if (policyFlags & POLICY_FLAG_FUNCTION) { metaState |= AMETA_FUNCTION_ON; }
设置metaState为meta键标志
policyFlags |= POLICY_FLAG_TRUSTED;
设置该标志表示按键事件是可信任的,比如,一个直接与Android机器相连接的按键设备
KeyEventevent; event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime);
把参数传递给KeyEvent对象event
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等,这一步将在第7节分析
needWake = enqueueInboundEventLocked(newEntry);
创建KeyEntry对象并把按键事件作为该对象的内容,enqueueInboundEventLocked函数根据输入事件的类型作进一步处理:
bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); traceInboundQueueLengthLocked(); switch (entry->type) { case EventEntry::TYPE_KEY: { // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // the app switch key. KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); if (isAppSwitchKeyEventLocked(keyEntry)) { if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { if (mAppSwitchSawKeyDown) { #if DEBUG_APP_SWITCH ALOGD("App switch is pending!"); #endif mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; mAppSwitchSawKeyDown = false; needWake = true; } } } break; }
mInboundQueue一开始没有数据为空,enqueueAtTail函数把按键事件对象KeyEntry插入到mInboundQueue队尾。
case语句用来切换app,如果是HOME、挂号按键,就把needWake设为true,表示可以唤醒InputDispatcherThread来分发按键事件,否则,不唤醒InputDispatcherThread,那么按键就不会处理
if (needWake) { mLooper->wake(); }
wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程是谁?在1.5.2.2节,InputDispatcherThread线程执行到了Looper对象的pollOnce函数时处于阻塞状态,此时epoll机制检测到管道中有数据写入事件发生,由这篇文章:Handler机制可知,读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread,Q6也就有了答案。
小结:
把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。
现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。
3 Framework层如何分发、传递给应用层
由2.2节可知,InputDispatcherThread线程被唤醒后,又一次地执行dispatchOnce函数,在dispatchOnce中调用了dispatchOnceInnerLocked函数3.1 InputDispatcher.cpp的dispatchOnceInnerLocked
mInboundQueue中已经有了按键事件,不为空,执行到这句:
else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); }
dequeueAtHead函数从队列mInboundQueue头部取出一个元素EventEntry对象交给mPendingEvent,EventEntry代表按键事件
在switch语句中,根据类型来选择执行语句,按键为TYPE_KEY
case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); ...... done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; }
忽略不重要的代码,下一步调用dispatchKeyLocked函数:
if (! entry->dispatchInProgress) { if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && (entry->policyFlags & POLICY_FLAG_TRUSTED) && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { // We have seen two identical key downs in a row which indicates that the device // driver is automatically generating key repeats itself. We take note of the // repeat here, but we disable our own next key repeat timer since it is clear that // we will not need to synthesize key repeats ourselves. entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; resetKeyRepeatLocked(); mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves } else { // Not a repeat. Save key down state in case we do see a repeat later. resetKeyRepeatLocked(); mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout; } mKeyRepeatState.lastKeyEntry = entry; entry->refCount += 1; } else if (! entry->syntheticRepeat) { resetKeyRepeatLocked(); } if (entry->repeatCount == 1) { entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; } else { entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; } entry->dispatchInProgress = true; logOutboundKeyDetailsLocked("dispatchKey - ", entry); }
先判断是否包含重复按键事件,如果是,暂时先清除掉,然后计算repeatCount值;如果没有重复按键事件,就保存按键状态、执行时间;如果计算后的repeatCount为1,设flags为AKEY_EVENT_FLAG_LONG_PRESS,表示长按事件。
entry->dispatchInProgress = true;
把dispatchInProgress设为true,意味着即将进行分发事件了
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); if (mFocusedWindowHandle != NULL) { commandEntry->inputWindowHandle = mFocusedWindowHandle; } commandEntry->keyEntry = entry; entry->refCount += 1; return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } }
entry->interceptKeyResult一开始默认为INTERCEPT_KEY_RESULT_UNKNOWN,if语句成立
如果entry->policyFlags为POLICY_FLAG_PASS_TO_USER,表示按键需要传递给应用程序;doInterceptKeyBeforeDispatchingLockedInterruptible函数也是拦截按键处理的方法,在7.2节详解
// Identify targets. Vector<InputTarget> inputTargets; int32_tinjectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); setInjectionResultLocked(entry, injectionResult);
InputTarget结构体:指定一个输入事件如何被分发到一个特定窗口。该结构体包含了很多特征变量:x,y坐标,输入事件通道InputChannel等;findFocusedWindowTargetsLocked找到获得焦点的窗口,如果成功,返回值设为INPUT_EVENT_INJECTION_SUCCEEDED,并把获得焦点的窗口的输入通道保存到InputTarget对象中,再把InputTarget对象放到inputTargets堆栈顶,setInjectionResultLocked函数把该获得焦点的标志INPUT_EVENT_INJECTION_SUCCEEDED保存到InjectionState结构体元素injectionResult中
// Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets);
dispatchEventLocked函数从inputTarget容器中取出获得焦点的输入通道管理对象Connection,Connection用来管理单个输入通道InputChannel的分发状态。dispatchEventLocked函数的调用过程:
dispatchEventLocked —-> prepareDispatchCycleLocked —-> enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_tcurrentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { bool wasEmpty = connection->outboundQueue.isEmpty(); // Enqueue dispatch entries for the requested modes. 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 the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.isEmpty()) { startDispatchCycleLocked(currentTime, connection); } }
outboundQueue一开始为空,当执行完enqueueDispatchEntryLocked后,outboundQueue就有了按键事件DispatchEntry,不为空,所以if语句:
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
成立,startDispatchCycleLocked开始发送事件,先看enqueueDispatchEntryLocked:
enqueueDispatchEntryLocked函数相关代码:
void InputDispatcher::enqueueDispatchEntryLocked( const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_tdispatchMode) { int32_tinputTargetFlags = inputTarget->flags; if (!(inputTargetFlags & dispatchMode)) { return; } inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor); // Apply target flags and update the connection's input state. 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)) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName()); #endif deletedispatchEntry; return; // skip the inconsistent event } break; } ...... } // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { incrementPendingForegroundDispatchesLocked(eventEntry); } // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); }
最后一个参数dispatchMode针对触摸的,与触摸相关的代码暂时去掉
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor);
创建DispatchEntry按键事件对象,把该对象作为一个发送数据包加入到outboundQueue队列中
trackKey函数把按键分为按下和松开两类事件处理,如果是按下就就先保存到KeyMemento结构体中,如果是在松开状态下,该事件是回退事件,即不在应用程序中处理的事件,就从回退事件集合中删除该按键
// Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry);
再把新构建的按键事件数据包添加到outboundQueue队列尾部即栈顶。
startDispatchCycleLocked函数:
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.isEmpty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.head; dispatchEntry->deliveryTime = currentTime; // Publish the event. status_tstatus; EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); // Publish the key event. 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; } }
当outboundQueue非空并且其他条件都具备时(比如,inputchannel已经注册,通信没有错误等),取出outboundQueue队列头元素,赋给dispatchEntry,再取出事件对象KeyEntry,根据事件类型确定case语句分支,如果是按键事件,就调用connection的InputPublisher的publishKeyEvent函数发送到inputchannel中,如果publishKeyEvent返回0,表示按键事件发送成功
3.2 InputTransport.cpp的publishKeyEvent
status_tInputPublisher::publishKeyEvent( uint32_tseq, int32_tdeviceId, int32_tsource, int32_taction, int32_tflags, int32_tkeyCode, int32_tscanCode, int32_tmetaState, int32_trepeatCount, nsecs_tdownTime, nsecs_teventTime) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," "downTime=%lld, eventTime=%lld", mChannel->getName().string(), seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); #endif if (!seq) { ALOGE("Attempted to publish a key event with sequence number 0."); return BAD_VALUE; } InputMessagemsg; 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; return mChannel->sendMessage(&msg); }
创建Message消息,把传递过来的事件信息保存到Message对象中,调用服务端管道InputChannel的sendMessage方法发送消息,sendMessage源码:
status_tInputChannel::sendMessage(const InputMessage* msg) { size_tmsgLength = msg->size(); ssize_tnWrite; do { nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), msg->header.type, error); #endif if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) { return DEAD_OBJECT; } return -error; } if (size_t(nWrite) != msgLength) { #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", mName.string(), msg->header.type); #endif return DEAD_OBJECT; } #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); #endif return OK; }
send系统调用相当于write调用,区别在于:send专用于socket文件描述符;send多了一个flags参数表示I/O事件的行为。send向服务端fd发送消息msg,如果成功,返回成功发送消息的大小;返回-1表示发送了错误
在3.4.1.1节通过socketpair创建一对socket对象,一个用作服务端,一个用作客户端,并且是已经连接好的插口,无需再通过bind、connect去连接,两端都可以发送、接收,全双工通信。在按键事件中,用socket[0]作为服务端,通过send向服务端发送数据,recv从客户端socket[1]接收数据。
在3.5.3.1节,socket客户端文件描述符被注册到了主线程epoll兴趣列表中,如果socket服务端没有往inputchannel中写入数据,客户端暂时处于阻塞状态,一旦服务端有了数据后,客户端也被唤醒了,客户端唤醒后的处理过程在第5节分析。
Q9 在publishKeyEvent中调用这句时:
return mChannel->sendMessage(&msg)
mChannel是服务端管道对象,该对象如何创建、注册的?socket文件描述符mFd什么时候创建;既然有服务端,肯定有客户端来接收数据,那么应用程序客户端socket如何创建?应用程序客户端InputChannel对象又是什么时候创建、注册的?
关于此问题将在第4节分析
4 Framework层服务端管道和应用程序客户端管道如何创建、注册
涉及到应用程序相关的,就从ViewRootImpl来分析4.1 ViewRootImpl.java的setView方法
if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); }
当窗口没有按键事件传输通道时,创建一个InputChannel对象,相当于一个管道,其他进程通过该管道像窗口发送按键事件消息。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
mWindowSession在ViewRootImpl构造器中创建:
mWindowSession = WindowManagerGlobal.getWindowSession();
4.2 WindowManagerGlobal.java的getWindowSession方法
public static IWindowSessiongetWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManagerimm = InputMethodManager.getInstance(); IWindowManagerwindowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { Log.e(TAG, "Failed to open window session", e); } } return sWindowSession; } }
getWindowManagerService方法源码:
public static IWindowManagergetWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { sWindowManagerService = getWindowManagerService(); ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); } catch (RemoteException e) { Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e); } } return sWindowManagerService; } }
getWindowSession方法做了很多工作,单例类InputMethodManager通过getInstance返回其唯一的对象,InputMethodManager表示输入法框架系统级API,用以输入法和应用程序之间的交互。
getService(“window”)获得一个WindowManagerService服务的Binder对象,实际是BinderProxy对象,asInterface接口根据BinderProxy对象生成Proxy客户端代理对象,返回到getWindowSession中赋给IWindowManager接口类型windowManager变量,然后利用该代理对象来访问WindowManagerService服务端中的方法openSession,其参数涵义:
第一个参数是一个IWindowSessionCallback.Stub对象
第二个参数imm.getClient(),返回IInputMethodClient.Stub对象
第三个参数imm.getInputContext(),返回ControlledInputConnectionWrapper对象
在openSession方法中,创建了Session对象,并把这三个参数及WindowManagerService对象传递到Session中
得到Session对象后,返回到4.1节ViewRootImpl构造器中赋给IWindowSession接口类型mWindowSession,再调用Session对象的addToDisplay
4.3 Session.java的addToDisplay方法
public int addToDisplay(IWindowwindow, int seq, WindowManager.LayoutParamsattrs, int viewVisibility, int displayId, RectoutContentInsets, RectoutStableInsets,InputChanneloutInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outInputChannel); }
mWindow:W对象,W实现了IWindow.Stub抽象类,继承了Binder类
mSeq:一个整型,每个按键事件的序号
mWindowAttributes:初始化语句有:
final WindowManager.LayoutParamsmWindowAttributes = new WindowManager.LayoutParams();
创建了一个布局参数对象
mAppVisible:确定mView即DecorView的可见性,默认为true,可见
mDisplay.getDisplayId():获得应用程序逻辑显示区域id号
mAttachInfo.mContentInsets:
final RectmContentInsets = new Rect();
一个矩形对象,作为窗口可见的矩形修饰
mAttachInfo.mStableInsets:
final RectmStableInsets = new Rect();
也是一个矩形对象,作为窗口固定的修饰
mInputChannel:待注册的管道对象
mService是WindowManagerService对象,在addToDisplay中调用WindowManagerService的addWindow方法
4.4 WindowManagerService.java的addWindow方法
与inputChannel相关部分:
if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); }
4.4.1 InputChannel的openInputChannelPair
openInputChannelPair的实现在本地方法android_view_InputChannel_nativeOpenInputChannelPair中:
static jobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, jclassclazz, jstringnameObj) { const char* nameChars = env->GetStringUTFChars(nameObj, NULL); String8name(nameChars); env->ReleaseStringUTFChars(nameObj, nameChars); sp<InputChannel> serverChannel; sp<InputChannel> clientChannel; status_tresult = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { String8message; message.appendFormat("Could not open input channel pair. status=%d", result); jniThrowRuntimeException(env, message.string()); return NULL; } jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); if (env->ExceptionCheck()) { return NULL; } jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(serverChannel)); if (env->ExceptionCheck()) { return NULL; } jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(clientChannel)); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(channelPair, 0, serverChannelObj); env->SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair; }
声明服务端管道serverChannel、客户端管道clientChannel,作为openInputChannelPair的参数
4.4.1.1 InputTransprot.cpp的openInputChannelPair
status_tInputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_tresult = -errno; ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.string(), errno); outServerChannel.clear(); outClientChannel.clear(); return result; } int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); String8serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; }
int sockets[2]; socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)
创建一个饱含2个元素的socket数组,socketpair创建一对socket对象,SOCK_SEQPACKET表示创建连续可靠的数据包连接,如果创建成功,返回0,如果返回-1,出错。
outServerChannel = new InputChannel(serverChannelName, sockets[0]); outClientChannel = new InputChannel(clientChannelName, sockets[1]);
以这一对socket文件描述符、通道名称作为参数分别创建服务端管道对象outServerChannel和客户端管道对象clientChannelName
在InputChannel构造函数中,把管道名称传给mName,socket文件描述符保存到mFd中,设置mFd为非阻塞模式
4.4.1.2 android_view_InputChannel.cpp的android_view_InputChannel_nativeOpenInputChannelPair
jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
创建包含2个元素的数组,数组对象类型是gInputChannelClassInfo.clazz即InputChannel类型,元素初始化为空
jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(serverChannel)); jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(clientChannel));
用已创建的服务端通道serverChannel和客户端通道clientChannel作为参数分别创建本地通道对象NativeInputChannel,android_view_InputChannel_createInputChannel:
static jobjectandroid_view_InputChannel_createInputChannel(JNIEnv* env, NativeInputChannel* nativeInputChannel) { jobjectinputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.ctor); if (inputChannelObj) { android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); } return inputChannelObj; }
创建Java层InputChannel类的对象保存到inputChannelObj中,如果成功,再调用android_view_InputChannel_setNativeInputChannel把刚才创建的NativeInputChannel对象指针赋给InputChannel对象中的变量Ptr,最后返回新创建的java层的InputChannel对象分别赋给serverChannelObj和clientChannelObj
env->SetObjectArrayElement(channelPair, 0, serverChannelObj); env->SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair;
用新创建的一对管道对象初始化数组channelPair,返回该数组赋给WindowManagerService中的inputChannels数组
小结:
makeInputChannelName创建了唯一的管道名称,openInputChannelPair以该名称作为参数创建一对管道InputChannel对象,存储在数组inputChannels中,inputChannels[0]作为服务端管道提供给inputdispatcher使用,用来发送按键事件;inputChannels[1]作为客户端管道提供给应用程序主线程使用,用来接收、消费按键事件。
4.4.2 InputManagerService的registerInputChannel
服务端管道inputChannels[0]创建好后,需要注册:
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
参数win.mInputWindowHandle是一个输入事件窗口的句柄,有多个作用:InputDispatcher在发送按键事件前会通过该句柄检查是否有获得焦点的窗口;如果有焦点的窗口,可以通过该句柄获得应用程序的InputChannel
registerInputChannel —-> nativeRegisterInputChannel
4.4.2.1 com_android_server_input_InputManagerService.cpp的nativeRegisterInputChannel函数
static void nativeRegisterInputChannel(JNIEnv* env, jclassclazz, jlongptr, jobjectinputChannelObj, jobjectinputWindowHandleObj, jbooleanmonitor) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { throwInputChannelNotInitialized(env); return; } sp<InputWindowHandle> inputWindowHandle = android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); status_tstatus = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor); if (status) { String8message; message.appendFormat("Failed to register input channel. status=%d", status); jniThrowRuntimeException(env, message.string()); return; } if (! monitor) { android_view_InputChannel_setDisposeCallback(env, inputChannelObj, handleInputChannelDisposed, im); } }
先获得本地InputChannel对象和InputWindowHandle对象,再调用NativeInputManager的registerInputChannel函数:
status_tNativeInputManager::registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { return mInputManager->getDispatcher()->registerInputChannel( inputChannel, inputWindowHandle, monitor); }
通过InputManager对象获得对应的InputDispatcher对象,进而调用其registerInputChannel函数注册
status_tInputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { #if DEBUG_REGISTRATION ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(), toString(monitor)); #endif { // acquire lock AutoMutex_l(mLock); if (getConnectionIndexLocked(inputChannel) >= 0) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().string()); return BAD_VALUE; } sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); if (monitor) { mMonitoringChannels.push(inputChannel); } mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock // Wake the looper because some connections have changed. mLooper->wake(); return OK; }
if (getConnectionIndexLocked(inputChannel) >= 0) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().string()); return BAD_VALUE; }
检查inputChannel是否注册,首次执行时,没有注册
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
通过创建的管道inputChannel以及窗口句柄inputWindowHandle创建Connection对象,Connection用来管理inputChannel
int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection);
getFd获得服务端管道inputChannel上的socket文件描述符,然后以键值对的方式把connect加入到KeyedVector中,这就完成了服务端通道的注册。
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
然后把socket服务端加入到epoll兴趣列表中,mLooper属于InputDispatcherThread,不属于主线程,注意与4.5.3.1节的区别,4.5.3.1节是注册到主线程对应的epoll对象中。ALOOPER_EVENT_INPUT表示可读事件,意思是在服务端socket中读取数据,这样设置的目的在于,当客户端socket接受到事件处理完后会向服务端发送一个反馈信号,服务端接收此信号后会回调handleReceiveCallback函数,该函数会再一次执行startDispatchCycleLocked函数从outboundQueue中取出按键事件进行发送,循环处理。
addFd函数的具体执行过程与4.5.4节几乎一样,不再分析。
4.4.3 InputChannel.java的transferTo
在4.4.2节已经注册了服务端管道inputChannels[0],那么客户端管道inputChannels[1]如何注册?
inputChannels[1].transferTo(outInputChannel);
public void transferTo(InputChanneloutParameter) { if (outParameter == null) { throw new IllegalArgumentException("outParameter must not be null"); } nativeTransferTo(outParameter); }
outInputChannel是InputChannel对象,inputChannels[1]也是刚刚创建的InputChannel对象,transferTo两头都是InputChannel对象,这又是怎么回事
transferTo的调用者是当前新创建客户端管道对象,参数outInputChannel是在ViewRootImpl中创建的,两者是不同的对象。
4.4.3.1 nativeTransferTo的本地实现
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobjectobj, jobjectotherObj) { if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Other object already has a native input channel."); return; } NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj); android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); android_view_InputChannel_setNativeInputChannel(env, obj, NULL); }
env:JNI接口对象
obj:本地方法所在对象的引用,就是inputChannels[1]客户端对象的引用
otherObj:在ViewRootImpl中创建的InputChannel对象
首先调用android_view_InputChannel_getNativeInputChannel函数
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env, jobjectinputChannelObj) { jlonglongPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr); return reinterpret_cast<NativeInputChannel*>(longPtr); }
传过来的对象时otherObj,其变量mPtr在初始化时为空,没有额外赋值,所以longPtr为空,这句:
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
不成立
又一次调用android_view_InputChannel_getNativeInputChannel函数,只不过第二个参数是obj即inputChannels[1],此时inputChannels[1]中的mPtr指针不为空,因此在3.4.1.2节中设置了mPtr为inputChannels[1]对应的NativeInputChannel地址,返回NativeInputChannel指针给nativeInputChannel变量,而后调用android_view_InputChannel_setNativeInputChannel函数把otherObj中的mPtr设为NativeInputChannel对象地址,这就表明ViewRootImpl中创建的InputChannel对象是客户端管道
读到此处可知,transferTo方法只是把ViewRootImpl中创建的InputChannel对象中的mPtr变量赋了值,该值就是客户端InputChannle对应的NativeInputChannel对象地址。
4.5 ViewRootImpl.java的setView方法
在4.4.2.1节,注册服务端管道InputChannel[0]时,创建了InputPublisher对象,用来发送消息到input channel,那么注册在客户端管道InputChannel[1]时,也会创建一个接收消息的对象,这就是InputConsumer,从管道中获取事件。客户端如何创建?还是在ViewRootImpl.java的setView方法中:
if (viewinstanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); }
view是DecorView对象,DecorView实现了RootViewSurfaceTaker接口,因此,view也是RootViewSurfaceTaker接口类型,if语句成立,willYouTakeTheInputQueue实际返回一个NativeActivity对象,因为NativeActivity实现了InputQueue.Callback接口
InputQueue:应用程序通过InputQueue来获得输入设备事件
InputQueue.Callback:当InputQueue绑定、解除绑定与线程关系时的回调接口
创建了InputQueue对象,作为NativeActivity中回调方法onInputQueueCreated的参数;用InputChannel和Looper对象作为参数创建WindowInputEventReceiver对象并用mInputEventReceiver指向该对象。
InputQueue构造器中调用了本地方法nativeInit
mPtr = nativeInit(new WeakReference<InputQueue>(this), Looper.myQueue());
第一个参数是InputQueue弱引用对象,第二个参数是主线程MessageQueue对象
4.5.1 android_view_InputQueue.cpp的nativeInit
static jlongnativeInit(JNIEnv* env, jobjectclazz, jobjectqueueWeak, jobjectjMsgQueue) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper()); if (!queue.get()) { jniThrowRuntimeException(env, "InputQueue failed to initialize"); return 0; } queue->incStrong(&gInputQueueClassInfo); return reinterpret_cast<jlong>(queue.get()); }
android_os_MessageQueue_getMessageQueue获得C++层NativeMessageQueue对象,也是MessageQueue对象,getLooper()获得Looper对象,queueWeak是java层传递过来的InputQueue弱引用对象,createQueue源码:
InputQueue* InputQueue::createQueue(jobjectinputQueueObj, const sp<Looper>& looper) { int pipeFds[2]; if (pipe(pipeFds)) { ALOGW("Could not create native input dispatching pipe: %s", strerror(errno)); return NULL; } fcntl(pipeFds[0], F_SETFL, O_NONBLOCK); fcntl(pipeFds[1], F_SETFL, O_NONBLOCK); return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]); }
创建了一个管道pipeFds,设置管道读、写端为非阻塞模式
创建了c++层InputQueue对象,赋给queue变量。InputQueue的构造函数:
InputQueue::InputQueue(jobjectinputQueueObj, const sp<Looper>& looper, int dispatchReadFd, int dispatchWriteFd) : mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd), mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) { JNIEnv* env = AndroidRuntime::getJNIEnv(); mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj); }
分别把管道读端、写端文件描述符赋给mDispatchReadFd、mDispatchWriteFd;用JNI接口方法NewGlobalRef创建inputQueueObj本地全局引用赋给mInputQueueWeakGlobal
4.5.2 NativeActivity的onInputQueueCreated
public void onInputQueueCreated(InputQueuequeue) { if (!mDestroyed) { mCurInputQueue = queue; onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); } }
把创建的InputQueue对象赋给mCurInputQueue,当创建好InputQueue后,既可回调onInputQueueCreated重写自己的代码,表明可以从InputQueue中获取事件
4.5.3 WindowInputEventReceiver的构造器
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper()); public WindowInputEventReceiver(InputChannelinputChannel, Looperlooper) { super(inputChannel, looper); }
public InputEventReceiver(InputChannelinputChannel, Looperlooper) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null"); } if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), inputChannel, mMessageQueue); mCloseGuard.open("dispose"); }
调用WindowInputEventReceiver的构造方法创建了一个WindowInputEventReceiver对象,其构造方法没有实现,直接调用父类InputEventReceiver的构造方法,第一个参数就是客户端管道对象,其内部mPtr变量指向了客户端NativeInputChannel对象地址。
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), inputChannel, mMessageQueue);
第一个参数this,实际类型是WindowInputEventReceiver类型,这个参数很重要,在5.3节会根据该类型决定调用哪个onInputEvent方法。
static jlongnativeInit(JNIEnv* env, jclassclazz, jobjectreceiverWeak, jobjectinputChannelObj, jobjectmessageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { jniThrowRuntimeException(env, "InputChannel is not initialized."); return 0; } sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue); status_tstatus = receiver->initialize(); if (status) { String8message; message.appendFormat("Failed to initialize input event receiver. status=%d", status); jniThrowRuntimeException(env, message.string()); return 0; } receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object return reinterpret_cast<jlong>(receiver.get()); }
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, jobjectreceiverWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false), mFdEvents(0) { }
用本地InputChannel, NativeMessageQueue及java层InputEventReceiver对象作为参数创建本地NativeInputEventReceiver对象并初始化,receiverWeak就是传递过来的WindowInputEventReceiver对象,赋给了mReceiverWeakGlobal变量。
创建好NativeInputEventReceiver对象后返回到java层InputEventReceiver中赋给mReceiverPtr变量,java层拥有这个变量就等于拥有了c++层NativeInputEventReceiver对象的指针
4.5.3.1 NativeInputEventReceiver的initialize函数
status_tNativeInputEventReceiver::initialize() { setFdEvents(ALOOPER_EVENT_INPUT); return OK; }
void NativeInputEventReceiver::setFdEvents(int events) { if (mFdEvents != events) { mFdEvents = events; int fd = mInputConsumer.getChannel()->getFd(); if (events) { mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL); } else { mMessageQueue->getLooper()->removeFd(fd); } } }
InputConsumer获取客户端inputChannel管道上的socket文件描述符添加到epoll对象兴趣列表中,fd是socket的客户端。mMessageQueue是主线程中的消息队列,getLooper返回主线程Looper对象,因此,客户端socket文件描述符被注册到了主线程中epoll对象中。
4.5.4 Looper.cpp的addFd函数
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback.get(), data); #endif if (!callback.get()) { if (! mAllowNonCallbacks) { ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { ALOGE("Invalid attempt to set NULL callback with ident < 0."); return -1; } } else { ident = POLL_CALLBACK; } int epollEvents = 0; if (events & EVENT_INPUT) epollEvents |= EPOLLIN; if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT; { // acquire lock AutoMutex_l(mLock); Requestrequest; request.fd = fd; request.ident = ident; request.callback = callback; request.data = data; struct epoll_eventeventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = epollEvents; eventItem.data.fd = fd; ssize_trequestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.replaceValueAt(requestIndex, request); } } // release lock return 1; }
int epollEvents = 0; if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
addFd中第2个参数为0,第3个参数是ALOOPER_EVENT_INPUT,第4个参数this,是NativeInputEventReceiver对象,也是LooperCallback对象类型,第5个参数是NULL
第一个if语句不成立,把ident设置成POLL_CALLBACK,表示调用者有回调方法会被执行。
int epollEvents = 0; if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
设置监控事件为EPOLLIN可读事件
Requestrequest; request.fd = fd; request.ident = ident; request.callback = callback; request.data = data; (base\services\core\jni) 58730 2015/6/6
用传递过来的参数初始化Request结构体变量request
struct epoll_eventeventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = epollEvents; eventItem.data.fd = fd;
声明epoll机制事件结构体变量eventItem并初始化,设events为可读事件,注册的文件描述符为socket服务端fd
ssize_trequestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.replaceValueAt(requestIndex, request); }
判断容器mRequests中是否有socket文件描述符fd,如果没有,就把fd注册到epoll对象mEpollFd中,否则,修改现有的epoll对象的兴趣列表中的fd,更新了eventItem。同时把调用者相关信息对象Request与fd作为一对映射关系保存到mRequests容器中,待执行epoll_wait时使用。
小结:
应用程序端管道inputChannels[1]注册在InputEventReceiver中,其socket对象注册到主线程epoll对象兴趣列表中,当socket对端即服务端inputChannels[0]上有数据写入后,应用程序便可从服务端socket对象上读取数据。
相关文章推荐
- 【转载两篇关于android按键事件传递的文章,流程非常完整】Android按键事件传递流程(二)
- 【转载两篇关于android按键事件传递的文章,流程非常完整】Android按键事件传递流程(一)
- Android按键事件传递流程(二)
- Android按键事件传递流程(二)
- Android按键事件传递流程(一)
- Android 4.0按键事件以及电源管理流程分析
- Android学习笔记-转载两篇关于线程更新UI的方法的文章
- 关于Android中Message机制的两篇文章
- 自定义外部按键实现android对按键事件的响应实现流程
- Android事件传递机制【按键事件】
- C语言关于产生随机数文章转载两篇(一)
- 关于Android触摸事件传递
- 转载一篇关于android wifi的好文章
- Android源码事件传递流程剖析
- Android按键事件处理流程 -- KeyEvent
- C语言关于产生随机数文章转载两篇(二)
- Android 上从外部应用注入按键事件流程分析
- 转载两篇关于程序员的文章
- Android事件传递机制【按键事件】
- android 应用监听输入法按键事件【比如搜索和回车键等】的整个流程分析