android power key 按下到 通知PowerManager亮灭屏的流程

按键和输入有关,就涉及到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().

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

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.

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

在NativeInputManager的构造过程中,会创建一个EventHub实例,并且把这个EventHub作为参数来创建InputManager对象。EventHub类是真正执行监控键盘事件操作的地方。android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。就是EventHub读取/dev/input节点。

从EventHub.cpp和InputManager.cpp开始,目录变成framworks\native\services\inputflinger\:

android power key 按下到 通知PowerManager亮灭屏的流程

其中有两个InputDispatcher,InputReader成员。其参数 dispatcherPolicy和readerPolicy其实都是NativeInputManager。

InputDispatcher类是负责把输入消息分发给当前**的Activity窗口的,而InputReader类则是通过EventHub类来实现读取输入事件的。键盘的输入消息也是InputDispatcher负责处理。

Eventhub类:

android power key 按下到 通知PowerManager亮灭屏的流程

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,原来以为是有逻辑上的含义,后面领悟到,原来是提示调用的人需要加锁调用。

android power key 按下到 通知PowerManager亮灭屏的流程

registerDeviceForEpollLocked中添加mEpollFd对输入设备的读监听。当设备插入,或使能设备,会触发这个函数。

以设备插入为例,讲如何触发registerDeviceForEpollLocked()的。就涉及到EventHub中一个重要的函数getEvents().后面会讲getEvents()是如何进入的。getEvents()通过epoll_wait监听和处理设备插拔(mInotifyFd),inputreader唤醒(mWakeReadPipeFD),输入设备的输入事件(device->fd)。

一开始是不能监听设备的输入事件的,要先检测到设备的插入。

先看getEvents的最上层逻辑(删除了其他细节),首先是一个无线for循环。通过epoll_wait获得通知,通知放入mPendingEventItems中。

android power key 按下到 通知PowerManager亮灭屏的流程

在最上层的for循环里,有一个while循环处理mPendingEventItems。当有设备插入,将mpendingINotify设置为true。

android power key 按下到 通知PowerManager亮灭屏的流程

将所有的pendingEvent处理完后,通过readNotifyLocked() 从mINotifyFd中读取数据,循环处理inotify_event,将dev/input/和event->name拼合为devname(准确说是设备文件名,而不是设备名),作为入参调用openDeviceLocked().

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

openDeviceLocked通过ioctl(devicepath:dev/input/eventX为入参)获取输入设备的各种信息,对devcie变量初始化和赋值。

然后通过registerDeviceForEpolledLocked()监控dev/input/eventX状态,将device添加到mDevices向量里。

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

监听设备输入文件后,再回来看getEvents函数。函数从设备输入文件读取数据,一个一个读出input_event,转换成RawEvent赋给入参buffer。

android power key 按下到 通知PowerManager亮灭屏的流程

从上面可以看出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文件定义。

android power key 按下到 通知PowerManager亮灭屏的流程

最终通过process  -> processKey() ->  getListener()->notifyKey(). 

android power key 按下到 通知PowerManager亮灭屏的流程

此Listener是什么呢?回忆一下:

android power key 按下到 通知PowerManager亮灭屏的流程

 android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

所以这个Listener就是InputDispatcher。

终于到了InputDispatcher分发阶段了!

android power key 按下到 通知PowerManager亮灭屏的流程

对于power按键,最核心的就是mpolicy->interceptKeyBeforQueueing.  mPolicy是什么?请返回看本篇最开始的NativeInputManager()->InputManager()->InputDispatcher() 这依次的初始化流程,NativeInputManager将自己传递给了InputDispatcher的mPolicy参数。

NativeInputManager其实是C++和java层的桥接。从C++层调用NativeInputManager的函数,其实目的就是为了跳到java层InputManagerManager.java中的函数。它通过jni的CallIntMethod调用NativeInputManager的同名函数。具体参见我的另一篇博客通过android inputManagerService 看JNI 函数注册 - ****博客

android power key 按下到 通知PowerManager亮灭屏的流程

在inputDispatcher中有三个队列如下,其输入事件处理流程还是比较复杂的。不过power key 触发亮灭屏的操作在interceptKeyBeforQueueing中就处理完了。所以围绕这三个队列的流程就后面有时间再学习。快点结束这篇吧,还有一篇关于PROXIMITY_SCREEN_OFF_WAKE_LOCK的博客,因为半路跳到inputManager而停下来了。这两篇结束后,想学学ims和sip,python也可以啊!

android power key 按下到 通知PowerManager亮灭屏的流程

 

回到interceptKeyBeforQueueing:

android power key 按下到 通知PowerManager亮灭屏的流程

这个mWindowsManagerCallbacks是什么呢?又要从开天辟地不久之后的startOtherServices说起.inputManager初始化后,通过setWindowManagerCallbacks设置mWindowsManagerCallbacks。入参是InputMonitor.

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

进入InputMonitor.interceptKeyBoforeQueueing()函数。调用的是mService.mPolicy.interceptKeyBeforeQueueing:

android power key 按下到 通知PowerManager亮灭屏的流程

看下面几张图,最终是进入PhoneWindowManager().interceptKeyBeforeQueueing():

 

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

android power key 按下到 通知PowerManager亮灭屏的流程

下面看PhoneWindowManager().interceptKeyBeforeQueueing(),根据Power键时按下还是松开,分别调用interceptPowerKeyDown,interceptPowerKeyUP

android power key 按下到 通知PowerManager亮灭屏的流程

result &=~ACTION_PASS_TO_USER.  默认不传到user。

当时按下power键时先进行初步处理,判断是否截屏,来电静音等等。

android power key 按下到 通知PowerManager亮灭屏的流程

如果前面都没有处理,即若没有消化掉power键,则继续处理,主要工作是在亮屏时按POWER键发送延时事件,触发长按处理。如果超时前抬起power键,这些事件会被撤销。

android power key 按下到 通知PowerManager亮灭屏的流程

超时后,长按事件将得到处理,分别进入powerLongPress(),和powerVeryLongPress():

 android power key 按下到 通知PowerManager亮灭屏的流程

这两个短按和长按关机键的处理函数,默认流程会提示关机。并且将handler设置为true。 

android power key 按下到 通知PowerManager亮灭屏的流程

接下来看当power键抬起,maxcount默认为1.所以不会走maxcount判断语句。默认走powerPress(),处理一次power按下。

android power key 按下到 通知PowerManager亮灭屏的流程

powerPress中,不支持多次按键,count一定是1.默认走SHORT_PRESS_POWER_GO_TO_SLEEP.

即走goToSlee().

android power key 按下到 通知PowerManager亮灭屏的流程

调用PowerManager goToSleep 休眠。具体怎么休眠,灭屏,就要看PowerManager了。

android power key 按下到 通知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了。