按键和输入有关,就涉及到InputManagerService。
有三个文件:inputManager.java,InputManagerService.java,inputManager.cpp.
InputManager.java在framwork/base/core/java/android/hardware/input/
InputManagerService.java在framwork/base/core/java/com/android/server/input
inputManager.cpp在framworks\native\services\inputflinger\
InputManager.java是客户端,通过binder向InputManagerService.java请求服务。InputManagerService.java是一个binder服务。InputManagerService.java封装cpp层的inputManager.cpp,并向其提供会回调。
SystemServer.java 在startOtherSerivce中,初始化InputManagerService,并调用器start函数。InputManagerService的构造函数中调用nativeInit().
nativeInit是什么函数呢?它有对应的native c++函数。
查找规律如下:
InputManagerService的完整包名如下:
com.android.service.input.InputManagerService.java。
将所有 . 修改为 _ ,java改为cpp。 即com_android_service_input_InputManagerService.cpp。 此文件在framwork/base/service/core/jni目录中。
InputManagerService.java的nativeInit(),对应的就是com_android_service_input_InputManagerService.cpp中的nativeInit().在此函数中初始化了NativeInputManager。NativeInputManager构造函数中初始化了InputManager.
在NativeInputManager的构造过程中,会创建一个EventHub实例,并且把这个EventHub作为参数来创建InputManager对象。EventHub类是真正执行监控键盘事件操作的地方。android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。就是EventHub读取/dev/input节点。
从EventHub.cpp和InputManager.cpp开始,目录变成framworks\native\services\inputflinger\:
其中有两个InputDispatcher,InputReader成员。其参数 dispatcherPolicy和readerPolicy其实都是NativeInputManager。
InputDispatcher类是负责把输入消息分发给当前激活的Activity窗口的,而InputReader类则是通过EventHub类来实现读取输入事件的。键盘的输入消息也是InputDispatcher负责处理。
Eventhub类:
Android所有的input设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,便会将事件写到这些节点下,同时对于外部输入设备(鼠标键盘等)的插拔还会引起这些节点的创建和删除。通Inotify和epoll实现对输入设备和输入事件的监控。上面代码中Inotify检测文件系统IN_DELETE,IN_CREATE,即创建和删除,在这里就监测是输入设备打开或者删除的事件。epoll监测多个文件(有无数据供读出、有无空间供写入)。两个如何关联?通过Inotify,输入设备插拔,将写入mInotifyFd文件。通过epoll,mEpollFd监控mInotifyFd的EPOLLIN,即读事件。所以只需要用epoll_wait查询mEpollFd,只要输入设备插拔都会退出epollWait阻塞,进行具体的工作。
mEpollFd还监控了读管道mWakeReadPipeFD。对应的写管道eventHub.wake()函数写入mWakeReadPipeFD。eventHub在nativeInputManger中作为参数传给inputManager,最终传给inputReader。inputRead会调用eventHub.wake(),触发eventHub的getevents()中的epoll_wait退出阻塞。
inotify详见:inotify_add_watch(2) - Linux manual page 等。
epoll 详见epoll_ctl(2) - Linux manual page 等。
mInotifyFd和mWakeReadPipeFD有数据都会通知mEpollFd。如何区分哪个文件有数据呢?eventHub中通过给epoll_ctl()的第三个参数eventItem的data参数指定不同的值来区分。
目前只是检测了输入设备的插拔,如何获取设备的各种输入呢?
围绕mEpollFd搜索代码,原来在registerDeviceForEpollLocked。顺便说下,最近看代码framwork层的WMS,PMS以及这个input模块,发现有很多函数都以Locked,原来以为是有逻辑上的含义,后面领悟到,原来是提示调用的人需要加锁调用。
registerDeviceForEpollLocked中添加mEpollFd对输入设备的读监听。当设备插入,或使能设备,会触发这个函数。
以设备插入为例,讲如何触发registerDeviceForEpollLocked()的。就涉及到EventHub中一个重要的函数getEvents().后面会讲getEvents()是如何进入的。getEvents()通过epoll_wait监听和处理设备插拔(mInotifyFd),inputreader唤醒(mWakeReadPipeFD),输入设备的输入事件(device->fd)。
一开始是不能监听设备的输入事件的,要先检测到设备的插入。
先看getEvents的最上层逻辑(删除了其他细节),首先是一个无线for循环。通过epoll_wait获得通知,通知放入mPendingEventItems中。
在最上层的for循环里,有一个while循环处理mPendingEventItems。当有设备插入,将mpendingINotify设置为true。
将所有的pendingEvent处理完后,通过readNotifyLocked() 从mINotifyFd中读取数据,循环处理inotify_event,将dev/input/和event->name拼合为devname(准确说是设备文件名,而不是设备名),作为入参调用openDeviceLocked().
openDeviceLocked通过ioctl(devicepath:dev/input/eventX为入参)获取输入设备的各种信息,对devcie变量初始化和赋值。
然后通过registerDeviceForEpolledLocked()监控dev/input/eventX状态,将device添加到mDevices向量里。
监听设备输入文件后,再回来看getEvents函数。函数从设备输入文件读取数据,一个一个读出input_event,转换成RawEvent赋给入参buffer。
从上面可以看出eventHub维护设备列表,读取输入事件。但是eventHub并不处理输入事件,而是通过getEvents返回给入参buffer。
接下来就要看谁调用的getEvents?是InputReader的loopOnce()调用。接着InputReader经过如下路径。
loopOnce()-> processEventLocked()-> processEventsForDeviceLocked()
然后进入InputDevice类的process()处理。
InputDevice如何被添加到mDevices向量中?当eventtype为Device_ADDED时loopOnce()-> processEventLocked()->addDeviceLocked()中的mDevices.add().
InputDevice类的process()函数调用inputMapper类的process函数处理。
inputMapper又是如何来的?loopOnce()-> processEventLocked()->addDeviceLocked()->createDeviceLocked()调用device->addMapper().
当设备是按键时,createDeviceLocked()调用device->addMapper()时会添加一个keyboardInputMapper。
那么现在进入keyboardMapper的process函数。keyboardMapper,InputDevice都在InputReader.cpp文件定义。
最终通过process -> processKey() -> getListener()->notifyKey().
此Listener是什么呢?回忆一下:
所以这个Listener就是InputDispatcher。
终于到了InputDispatcher分发阶段了!
对于power按键,最核心的就是mpolicy->interceptKeyBeforQueueing. mPolicy是什么?请返回看本篇最开始的NativeInputManager()->InputManager()->InputDispatcher() 这依次的初始化流程,NativeInputManager将自己传递给了InputDispatcher的mPolicy参数。
NativeInputManager其实是C++和java层的桥接。从C++层调用NativeInputManager的函数,其实目的就是为了跳到java层InputManagerManager.java中的函数。它通过jni的CallIntMethod调用NativeInputManager的同名函数。具体参见我的另一篇博客通过android inputManagerService 看JNI 函数注册 - CSDN博客
在inputDispatcher中有三个队列如下,其输入事件处理流程还是比较复杂的。不过power key 触发亮灭屏的操作在interceptKeyBeforQueueing中就处理完了。所以围绕这三个队列的流程就后面有时间再学习。快点结束这篇吧,还有一篇关于PROXIMITY_SCREEN_OFF_WAKE_LOCK的博客,因为半路跳到inputManager而停下来了。这两篇结束后,想学学ims和sip,python也可以啊!
回到interceptKeyBeforQueueing:
这个mWindowsManagerCallbacks是什么呢?又要从开天辟地不久之后的startOtherServices说起.inputManager初始化后,通过setWindowManagerCallbacks设置mWindowsManagerCallbacks。入参是InputMonitor.
进入InputMonitor.interceptKeyBoforeQueueing()函数。调用的是mService.mPolicy.interceptKeyBeforeQueueing:
看下面几张图,最终是进入PhoneWindowManager().interceptKeyBeforeQueueing():
下面看PhoneWindowManager().interceptKeyBeforeQueueing(),根据Power键时按下还是松开,分别调用interceptPowerKeyDown,interceptPowerKeyUP
result &=~ACTION_PASS_TO_USER. 默认不传到user。
当时按下power键时先进行初步处理,判断是否截屏,来电静音等等。
如果前面都没有处理,即若没有消化掉power键,则继续处理,主要工作是在亮屏时按POWER键发送延时事件,触发长按处理。如果超时前抬起power键,这些事件会被撤销。
超时后,长按事件将得到处理,分别进入powerLongPress(),和powerVeryLongPress():
这两个短按和长按关机键的处理函数,默认流程会提示关机。并且将handler设置为true。
接下来看当power键抬起,maxcount默认为1.所以不会走maxcount判断语句。默认走powerPress(),处理一次power按下。
powerPress中,不支持多次按键,count一定是1.默认走SHORT_PRESS_POWER_GO_TO_SLEEP.
即走goToSlee().
调用PowerManager goToSleep 休眠。具体怎么休眠,灭屏,就要看PowerManager了。
1.亮屏 down
发delay事件
长按 : 弹出关闭重启对话框 delay事件到期触发
短按 :不做处理,会在up时清除delay事件。
亮屏 up
长按 :不做处理(因为power long press和powerVeryLongPress已经处理了power键,并将handle设置为true了。)
短按 调用goToSleep休眠系统
2. 灭屏 down
调用wakeup 唤醒系统:wakeUPFromPowerKey,设置handle为true
灭屏 up
不做处理:因为灭屏down时设置handle为true了。