Android事件处理流程分析
2014-05-28 13:25
405 查看
本文主要基于Android4.4源码的事件处理流程进行了分析。并参照了如下文章并引用了部分资源。
http://blog.csdn.net/windskier/article/details/6966264 http://www.eoeandroid.com/home.php?mod=space&uid=10407&do=blog&id=5070 http://blog.sina.com.cn/s/blog_89f592f50101394l.html http://blog.csdn.net/luoshengyang/article/details/6882903
事件处理流程
1)InputManager负责读取事件并把事件送到frameworks的java层
2)WindowManagerService里会有一个InputMonitor类来监听事件变化并做相应的分发处理。
3)在WindowManagerService会有一个WindowManagerPolicy来做消息拦截处理。
4)WindowManagerService会把消息发给最上面运行的窗口接收
SystemServer.java
(1)创建了InputManagerSercice和WindowManagerService的实例
(2)将InputManagerServcie的引用传递给WindowManagerService
(3)在InputManagerSercice中设定了WindowManagerService的回调函数
—— 将WindowManagerService的成员InputMonitor赋值给InputManagerSercice的成员InputManagerSercice的成员mWindowManagerCallbacks
(4)启动InputManagerSercice
创建InputManagerSercice的主要代码如下:
InputManagerService也有一个成员mHander,它与wmHandler运行在同一个Looper中。
最重要的是nativeInit,实现如下:
com_android_server_Input_InputManagerService.cpp
这里的MessageQueue和Looper都是wmHandler运行的Thread中的。
在native创建了NativeInputManager,并把指针返回给Java层。
NativeInputManager构造的时候,会创建了一个EventHub实例,并且把这个EventHub作为参数来创建InputManager对象。注意,这里的InputManager类是定义在C++层的,和前面在Java层的InputManager不一样,不过它们是对应关系。EventHub类是真正执行监控键盘事件操作的地方。
InputManager实例的创建过程,会InputManager类的构造函数里面执行一些初始化操作。
InputManager.cpp
这里主要是创建了一个InputDispatcher对象和一个InputReader对象,并且分别保存在成员变量mDispatcher和mReader中。InputDispatcher类是负责把事件消息向上层传递,而InputReader类则是通过EventHub类来实现读取事件的。
InputDispatcher在构造的时候,会创建一个Looper对象,Looper的核心其实是一个消息队列,通过不停的处理Looper消息队列中的消息来完成线程间的通信。这个Looper对象以后会用到。
创建了这两个对象后,还要调用initialize函数来执行其它的初始化操作。
这个函数创建了一个InputReaderThread线程实例和一个InputDispatcherThread线程实例,并且分别保存在成员变量mReaderThread和mDispatcherThread中。这里的InputReader实列mReader就是通过这里的InputReaderThread线程实例mReaderThread来读取事件的,而InputDispatcher实例mDispatcher则是通过这里的InputDispatcherThread线程实例mDisptacherThread来分发事件的。
至此,InputManagerService的创建工作就完成了。也生成了InputManager相关的一系列对象。下一步的主要工作就是启动InputManager了。
com_android_server_Input_InputManagerService.cpp
这里的 im->getInputManager()->start()实际上调用的就是InputManager的start方法。
InputManager.cpp
到这里,Input子系统的初始化过程就完毕了。
在Activity启动的时候,会通过windowmanager的addView方法将窗口加入到WMS中,并创建了ViewRoot,然后会调用ViewRoot的setView方法。
以上函数中与通道建立相关的地方有三处:
(1)requestLayout。
requestLayout函数来通知InputManager,这个Activity窗口是当前被激活的窗口。也就是更新激活窗口
(2)mWindowSession.addToDisplay
负责把事件接收通道的一端注册在InputManager中,也就是WMS注册InputChannel
(3)
也就是应用程序进程注册InputChannel。
需要注意的是:实际上WMS侧的addWindow比relayoutWindow先执行。
在relayoutWindow中调用了以下代码:
这个函数将当前系统中带有InputChannel的Activity窗口都设置为InputManager的输入窗口。这些窗口保存在mInputWindowHandles数组中。
InputManagerService.java
接下来会调用native的setInputWindows方法。
com_android_server_Input_inputManagerService.cpp
这个函数首先将Java层的Window转换成C++层的windowHandle,然后放在windowHandles向量中,最后将这些输入窗口设置到InputDispatcher中去。
InputDispatcher.cpp
这个方法的主要作用就是从传进来的窗口向量中,找到哪一个窗口是获得焦点的,获得焦点的输入窗口即为当前激活的窗口,并把窗口的句柄保存在mFocusedWindowHandle中。它的定义如下:
// Focus tracking for keys, trackball, etc.
sp<InputWindowHandle> mFocusedWindowHandle;
这里的outInputChannel是在setView时new并传入到addWindow中的。win是WindowState对象,运行在WMS侧。
通过InputChannel.openInputChannelPair函数来创建一对输入通道,其中一个位于WindowManagerService中,另外一个通过outInputChannel参数返回到应用程序中:
接下来我们就看一下InputChannel.openInputChannelPair函数的实现。
native实现
这个函数根据传进来的参数name在C++层分别创建两个InputChannel,一个作为Server端使用,一个作为Client端使用,这里的Server端即是指InputManager,而Client端即是指应用程序。这两个本地的InputChannel是通过InputChannel::openInputChannelPair函数创建的,创建完成后,再相应地在Java层创建相应的两个InputChannel,然后返回。
openInputChannelPair的实现如下:
先看一下InputChannel的构造函数
通过调用Linux的socketpair()方法建立一对匿名的已经连接的套接字,然后调用setsockopt()方法为其分配内存,然后用其做为参数,创建native环境中的InputChannel对象,分别赋值给outServerChannel、和outClientChannel指针。
创建好两个native层的InputChannel对象后,存在channelPair数组中,然后通过JNI返回到JAVA环境中。
下一步,需要把刚才创建的Server端的输入通道注册到InputManager中:
native的实现
这个函数主要是调用了InputDispatcher的registerInputChannel来真正执行注册输入通道的操作。
当需要通过管道发送消息的时候,从该列表中取出Connection对象,该对象与客户端是相对应的,之后调用mLooper->addFd()方法,把InputChannel对应的描述符添加到mLooper内部的描述符列表中。这里完成了wms的InputChannel的注册,即serverChannel。
在Looper类内部,会创建一个管道,然后Looper会睡眠在这个管道的读端,等待另外一个线程来往这个管道的写端写入新的内容,从而唤醒等待在这个管道读端的线程,除此之外,Looper还可以同时睡眠等待在其它的文件描述符上,因为它是通过Linux系统的epoll机制来批量等待指定的文件有新的内容可读的。这些其它的文件描述符就是通过Looper类的addFd成函数添加进去的了,在添加的时候,还可以指定回调函数,即当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个hanldeReceiveCallback函数。
至此,Server端的InputChannel就注册完成了。
2.3 应用程序进程注册InputChannel
接着调用父类的构造函数
JNI层
执行初始化函数
服务端和客户端都注册了Channel,最终都向Looper中加入了文件描述符。服务端使用的Channel是InputDispatcher创建的时候生成的,这里没有Java Looper,只有native Looper。
而客户端的Looper有一个Java的Looper和一个native Looper。
JAVA Looper包含一个MessageQueue,MessageQueue对应的Native 实例是一个NativeMessageQueue实例,NativeMessageQueue在创建的时候生成一个Native Looper。
客户端UI主线程循环读取Java Looper的消息以进行下一步的处理。
其实Native Looper存在的意义就是作为JAVA Looper机制的开关器,
1. 当消息队列中有消息存入时,唤醒Natvice Looper,至于如何发送向线程消息,这就用到了Handler;
2. 当消息队列中没有消息时或者消息尚未到处理时间时,Natvice Looper block住整个线程。
也就是说:创建了JAVA Looper的线程只有在有消息待处理时才处于活跃状态,无消息时block在等待消息写入的状态。Looper相关的内容可以参考 Android消息处理机制(Handler、Looper、MessageQueue与Message)
在以上客户端(ViewRoot)注册的时候,其实没有使用Handler向java的消息队列中push消息,而只是复用了native Looper。具体的过程如下:
UI主线程运行在Looper.loop();
以上通过queue.next()取得消息队列中的内容
接着调用nativePollOnce
这里借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。epoll机制这里不多介绍了。
这里的int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);就是等待文件描述符指向的文件的内容发生变化。
这些文件描述符是通过Looper.addFd添加进去的。如果制定了回调函数,这样当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个回调函数。
这里再介绍一下Looper.addFd。
创建SimpleLooperCallback时,会把这个函数指针赋值给它的成员mCallback,这个类有一个成员函数handleEvent,调用handleEvent就相当于调用注册进来的CallBack。
最后都是通过以下形式把对象作为参数传递给addFd的。WMS注册Channel的场景传递的是CallBack是函数指针,所以需要new一个SimpleLooperCallback.
应用程序进程注册Channel的场景直接传递的就是继承了LooperCallback的NativeInputEventReceiver对象,它实现了handleEvent方法。
实际上是把callback赋值给了request.callback,并保存在mRequests中。
在 Looper::pollInner函数中,当有文件描述符指向的文件发生变化时,也就是说,从epoll_wait时,最终会调用response.request.callback->handleEvent(fd, events, data);
对于WMS注册Channel的场景,就会调用之前addFd中的callback,即InputDispatcher::handleReceiveCallback方法。
对于应用程序注册Channel的场景,就会调用NativeInputEventReceiver::handleEvent方法。
关于这两个方法的实现,之后再做介绍。
下一步,先分析Input子系统的事件处理过程。
分几个部分介绍:
1. InputReader处理
2. InputDispatcher处理
nputReaderThread线程会不民地循环调用InputReader.pollOnce函数来读入事件,而实际的事件读入操作是由EventHub.getEvent函数来进行的。如果当前没有事件发生,InputReaderThread线程就会睡眠在EventHub.getEvent函数上,而当事件发生后,就会执行processEventsLocked函数进一步处理。
1.当EventHub尚未打开input系统eventXX设备时,InputReader去向EventHub获取事件时,EventHub会首先去打开所有的设备,并将每个设备信息以RawEvent的形式返给InputReader,也就是processEventsLocked()中处理的EventHubInterface::DEVICE_ADDED类型,该过程会根据每个设备的deviceId去创建InputDevice,并根据设备的classes来创建对应的InputMapper。
2.当所有的设备均被打开之后,InputReader去向EventHub获取事件时,EventHub回去轮询event节点,如果有事件,则会调用processEventsForDeviceLocked进行下一步的处理。
这里主要是调用 mapper->process,mapper相关的信息保存在mMappers数组中,InputReader会利用它来管理所收到的 input event,这些mapper 有 SwitchInputMapper,
VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper等等。
这些mapper是什么时候创建并添加到数组中的?主要是在DEVICE_ADDED时添加进去的。调用过程大致如下:
InputReader.cpp
processEventsLocked
-》addDeviceLocked
-》createDeviceLocked
-》 device = new InputDevice
device->addMapper
具体过程不做细致的分析了,总之,InputReader维护了mDevices和mMappers,到有事件发生时,InputReader会遍历所有的mapper,并调用对应的process方法。
本文主要讲解hard key(比如power,home,back,menu, volume up down等),对应的mapper是KeyboardInputMapper。所以事件到来时,会调用KeyboardInputMapper的process方法。
这里的主要处理语句是processKey。
这个函数首先对对按键作一些处理,例如,当某一个DPAD键被按下时,根据当时屏幕方向的不同,它所表示的意义也不同,因此,这里需要根据当时屏幕的方向来调整键盘码,如果这个键是一直按着不放的,不管屏幕的方向如何,必须保证后面的键盘码和前面的一样,如果是第一次按下某个键,还必须把它保存在mLocked.keyDowns里面,就是为了处理上面讲的当这个键盘一直按着不放的时候屏幕方向发生改变的情况。如果是松开键盘上的某个键,就把它从mLocked.keyDowns里面删除。
最后,调用getListener()->notifyKey通知InputDispatcher,有key事件发生了。
getListener()->notifyKey如何通知InputDispatcher?下面分析这一过程。
InputManager在构造的时候会创建InputReader。
此时,mDispatcher的作为参数传递给Reader
再来看一下NotifyArgs,它用来描述输入事件,根据不同的输入事件类型,也存在几个派生的NotifyArgs,比如NotifyKeyArgs,NotifyMotionArgs,NotifySwitchArgs等等,这里使用的是NotifyKeyArgs。
再来看一下getListener的实现。
接着调用QueuedInputListener的notifyKey,传入的参数是创建的NotifyKeyArgs的地址。
这里重新构造了一个NotifyKeyArgs,并push到QueuedInputListener的成员mArgsQueue向量里。
这个过程执行完之后,返回到InputReader::loopOnce的最后一条语句mQueuedListener->flush();
Key Event会调用NotifyKeyArgs::notify
这里就会调用InputDispatcher的notifykey,参数是NotifyKeyArgs。
这样就把事件通知给了dispathcer。
下一步分析dispatcher的处理流程。
函数首先是调用validateKeyEvent函数来验证action参数是否正确。正确的action参数的值只能为AKEY_EVENT_ACTION_DOWN(按下)或者AKEY_EVENT_ACTION_UP(松开)。
另外,InputDispatcher会截取这个按键事件,根据当前设备的状况来优先消化这个事件,这个过程当然是在将事件dispatch给ViewRoot之前。最终该过程交由interceptKeyBeforeQueueing()@PhoneWindowManager.java来处理。interceptKeyBeforeQueueing()主要是对一些特殊案件的特殊处理,并判断该按键是够应该传递给ViewRoot。通过设置标志位policyFlags的值来判断是否给ViewRoot,例如policyFlags&POLICY_FLAG_PASS_TO_USER
== 1 则应该传递给ViewRoot。
interceptKeyBeforeQueueing()特殊处理主要是针对在锁屏或者屏幕不亮的情况的下收到特殊的键值,如音量键或者wake键。wake键是指能够点亮屏幕的键时的操作。
下面再分析一下mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);是如何调用到interceptKeyBeforeQueueing()@PhoneWindowManager.java。
这里的mPolicy实际上是NativeInputManager的引用,是在NativeInputManager构造过程中创建InputManager时作为参数最终传递给InputDispatcher的成员mPolicy的。
所以执行下面的过程:
mWindowManagerCallbacks实际是一个InputMonitor对象,是在开机是有SystemServer调用的,
下面再回到InputDispatcher::notifyKey的处理方法继续分析。
接下来,按键马上进入第二轮处理。如果用户在Setting->Accessibility 中选择打开某些功能,比如说手势识别,Android的AccessbilityManagerService(辅助功能服务) 会创建一个 InputFilter 对象,它会检查输入的事件,根据需要可能会转换成新的Event,比如说两根手指头捏动的手势最终会变成ZOOM的event. 目前,InputManagerService 只支持一个InputFilter, 新注册的InputFilter会把老的覆盖。InputFilter
运行在SystemServer 的 ServerThread 线程里(除了绘制,窗口管理和Binder调用外,大部分的System Service 都运行在这个线程里)。而filterInput() 的调用是发生在Input Reader线程里,通过InputManagerService 里的 InputFilterHost 对象通知另外一个线程里的InputFilter 开始真正的解析工作。所以,InputReader 线程从这里结束一轮的工作,重新进入epoll_wait() 等待新的用户输入。InputFilter
的工作也分为两个步骤,首先由InputEventConsistencyVerifier 对象(InputEventConsistencyVerifier.java)对输入事件的完整性做一个检查,检查事件的ACTION_DOWN 和 ACTION_UP 是否一一配对。如果在Android Logcat 里看到过以下一些类似的打印:"ACTION_UP but key was not down." 就出自此处。接下来,进入到AccessibilityInputFilter 的 onInputEvent(),这里将把输入事件(主要是MotionEvent)进行处理,根据需要变成另外一个Event,然后通过sendInputEvent()将事件发回给InputDispatcher。最终调用到injectInputEvent()
将这个事件送入 mInBoundQueue。
其实,事件的过滤和拦截采用了策略模式机制进行不同的事件处理,由类图中的InputManager、InputFilter、PhoneWindowManager、Callbacks、InputMonitor等JAVA对象共同完成事件的过滤和拦截;Callbacks、InputMonitor主要负责回调事件的转发,InputFilter负责事件过滤请求的处理;应用可以通过InputManager对象的injectInputEvent函数完成事件的插入;PhoneWindowManager对象负责派发事件的拦截。事件的过滤请求和拦截请求流程相似,只是目的地不一样。
然后,将事件存储在一个输入队列mInboundQueue中:
最后,唤醒InputDispatccherThread线程
下面分析InputDispatcherThread的处理流程。
这里主要处理是
这里涉及到事件的丢弃,并不是所有的InputReader发送来的事件我们都需要传递给应用,比如上节讲到的翻盖/滑盖事件,除此之外的按键,触屏,轨迹球(后两者统一按motion事件处理),也会有部分的事件被丢弃,InputDispatcher总会根据一些规则来丢弃掉一部分事件,我们来简单分析一下哪些情况下我们需要丢弃掉部分事件?
InputDispatcher.h中定义了一个包含有丢弃原因的枚举:
不需要丢弃
2. DROP_REASON_POLICY
设置为DROP_REASON_POLICY主要有两种情形:
A. 在InputReader notify InputDispatcher之前,Policy会判断不需要传递给应用的事件。
B. 在InputDispatcher dispatch事件前,PhoneWindowManager使用方法interceptKeyBeforeDispatching()提前consume掉一些按键事件。
interceptKeyBeforeDispatching()主要对HOME/MENU/SEARCH按键的特殊处理,如果此时能被consume掉,那么在InputDispatcher 中将被丢弃。
3.DROP_REASON_APP_SWITCH
当有App switch 按键如HOME/ENDCALL按键发生时,当InputReader向InputDispatcher 传递app switch按键时,会设置一个APP_SWITCH_TIMEOUT 0.5S的超时时间,当0.5s超时时,InputDispatcher 尚未dispatch到这个app switch按键时,InputDispatcher 将会丢弃掉mInboundQueue中所有处在app switch按键前的按键事件。这么做的目的是保证app switch按键能够确保被处理。此时被丢弃掉的按键会被置为DROP_REASON_APP_SWITCH。
4. DROP_REASON_DISABLED
这个标志表示当前的InputDispatcher 被disable掉了,不能dispatch任何事件,比如当系统休眠时或者正在关机时会用到。
5. DROP_REASON_BLOCKED
如果事件传递后,已经进入了另外的应用,就把以前的事件丢弃。
6.DROP_REASON_STALE
设置的超时是10秒。如果超过了10秒,事件没有处理掉,那么就直接丢弃。
我们主要分析事件传递到ViewRoot的过程接着往下分析。
主要的处理是dispatchKeyLocked。
接下来会查找FocusedWindow,然后调用dispatchEventLocked。
从它的outboundQueue队列中取出当前需要处理的事件,然后调用 connection->inputPublisher.publishMotionEvent通知Channel的接收端。
InputTransport.cpp
调用::send进行系统调用(加::是为了能够正确的调用系统中的send,避免该类中刚好也有一个 close 函数的情况),通过send发送了一个socket消息,那么一定会有另一个socket接收消息,接收消息的就是Channel的另一端——UI主线程。UI主线程一直阻塞在epoll_wait,是在Looper::pollInner中调用的。
在应用程序注册InputChannel一节介绍过,当有事件到来的时候会调用哪个NativeInputEventReceiver::handleEvent方法。
1.
2.
3.
WindowInputEventReceiver继承自InputEventReceiver
以上分析的是从WMS到应用程序的过程。
下面简单分析一下从应用程序到WMS的过程。
当ViewRoot处理完事件后,都会调用finishInputEvent ,下面就从这个函数开发分析ViewRoot-》wms的过程。
在WMS注册Input Channel一节提到过,当WMS收到Socket消息后会调用handleReceiveCallback。
最主要的处理是以下两句:
1. status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
调用mChannel->receiveMessage用于获取消息。
2. d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
发送的 finish message之后, 就马上去呼叫startDispatchCycleLocked 函數去檢查 outboundQueue
里面还有沒有新的input event. 若有的话就放入InputMessage 共享內存, 然後通知 ViewRootImpl 去共享內存抓取新的 input
event. 若沒有新的 input event, 就不做事等待有新的input event进入 outboundQueue.
http://blog.csdn.net/windskier/article/details/6966264 http://www.eoeandroid.com/home.php?mod=space&uid=10407&do=blog&id=5070 http://blog.sina.com.cn/s/blog_89f592f50101394l.html http://blog.csdn.net/luoshengyang/article/details/6882903
事件处理流程
1)InputManager负责读取事件并把事件送到frameworks的java层
2)WindowManagerService里会有一个InputMonitor类来监听事件变化并做相应的分发处理。
3)在WindowManagerService会有一个WindowManagerPolicy来做消息拦截处理。
4)WindowManagerService会把消息发给最上面运行的窗口接收
1. Input子系统的初始化过程
系统开机的时候,SystemServer会启动InputManagerSercice和WindowManagerService。SystemServer.java
Slog.i(TAG, "Input Manager"); inputManager = new InputManagerService(context, wmHandler); Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, display, inputManager, wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); ActivityManagerService.self().setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start();上述过程主要做了如下事情:
(1)创建了InputManagerSercice和WindowManagerService的实例
(2)将InputManagerServcie的引用传递给WindowManagerService
(3)在InputManagerSercice中设定了WindowManagerService的回调函数
—— 将WindowManagerService的成员InputMonitor赋值给InputManagerSercice的成员InputManagerSercice的成员mWindowManagerCallbacks
(4)启动InputManagerSercice
1.1 创建InputManagerSercice
创建InputManagerSercice时传递的参数有一个wmHander,这个handler是运行在以下Thread中的// Create a handler thread just for the window manager to enjoy. HandlerThread wmHandlerThread = new HandlerThread("WindowManager"); wmHandlerThread.start();
创建InputManagerSercice的主要代码如下:
public InputManagerService(Context context, Handler handler) { this.mContext = context; this.mHandler = new InputManagerHandler(handler.getLooper()); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); mAppRSMouseMode = MOUSE_MODE_DEFAULT; mAppStylusFingerOnlyMode = true; mCurrentStylusFingerOnlyMode = true; mCurrentTouchLocked = false; mUserSetup = false; }
InputManagerService也有一个成员mHander,它与wmHandler运行在同一个Looper中。
最重要的是nativeInit,实现如下:
com_android_server_Input_InputManagerService.cpp
static jint nativeInit(JNIEnv* env, jclass clazz, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { 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<jint>(im); }
这里的MessageQueue和Looper都是wmHandler运行的Thread中的。
在native创建了NativeInputManager,并把指针返回给Java层。
NativeInputManager构造的时候,会创建了一个EventHub实例,并且把这个EventHub作为参数来创建InputManager对象。注意,这里的InputManager类是定义在C++层的,和前面在Java层的InputManager不一样,不过它们是对应关系。EventHub类是真正执行监控键盘事件操作的地方。
NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper) { 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.mouseMode = 0; mLocked.useRSMouse = false; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; mLocked.showStylus = false; mLocked.showEraser = false; mLocked.penToTouchDelayMs = -1; mLocked.lockTouch = false; mLocked.fingerOnlyMode = true; mLocked.stylusLockOnEdge = false; mLocked.leftHandedMode = false; } sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this);
InputManager实例的创建过程,会InputManager类的构造函数里面执行一些初始化操作。
InputManager.cpp
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(); }
这里主要是创建了一个InputDispatcher对象和一个InputReader对象,并且分别保存在成员变量mDispatcher和mReader中。InputDispatcher类是负责把事件消息向上层传递,而InputReader类则是通过EventHub类来实现读取事件的。
InputDispatcher在构造的时候,会创建一个Looper对象,Looper的核心其实是一个消息队列,通过不停的处理Looper消息队列中的消息来完成线程间的通信。这个Looper对象以后会用到。
创建了这两个对象后,还要调用initialize函数来执行其它的初始化操作。
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
这个函数创建了一个InputReaderThread线程实例和一个InputDispatcherThread线程实例,并且分别保存在成员变量mReaderThread和mDispatcherThread中。这里的InputReader实列mReader就是通过这里的InputReaderThread线程实例mReaderThread来读取事件的,而InputDispatcher实例mDispatcher则是通过这里的InputDispatcherThread线程实例mDisptacherThread来分发事件的。
至此,InputManagerService的创建工作就完成了。也生成了InputManager相关的一系列对象。下一步的主要工作就是启动InputManager了。
1.2 启动InputManagerService
InputManagerService的start函数主要是调用了nativeStart, 并把之前保存的NativeInputManager的指针mPtr传递下去。com_android_server_Input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_t result = im->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } }
这里的 im->getInputManager()->start()实际上调用的就是InputManager的start方法。
InputManager.cpp
status_t InputManager::start() { status_t result = 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; }这里启动之前创建的两个线程,用于读取和分发。然后就进入他们的线程函数threadLoop中。
到这里,Input子系统的初始化过程就完毕了。
2. 应用程序UI与Input子系统关系的建立——创建Channel
当接收到事件后,需要把事件传递给对应的窗口,就需要建立起一个通道,用于它们之间的通信。在Activity启动的时候,会通过windowmanager的addView方法将窗口加入到WMS中,并创建了ViewRoot,然后会调用ViewRoot的setView方法。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { ...... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } ...... if (view instanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); try { Activity activity = (Activity)view.getContext(); ActivityInfo ai = activity.getPackageManager().getActivityInfo( activity.getComponentName(), PackageManager.GET_META_DATA); mInputEventReceiver.mConsumeBatchesImmediately = ai.metaData.getBoolean("com.nvidia.immediateInput"); } catch(Exception e) { } } ...... } } }
以上函数中与通道建立相关的地方有三处:
(1)requestLayout。
requestLayout函数来通知InputManager,这个Activity窗口是当前被激活的窗口。也就是更新激活窗口
(2)mWindowSession.addToDisplay
负责把事件接收通道的一端注册在InputManager中,也就是WMS注册InputChannel
(3)
if (view instanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
也就是应用程序进程注册InputChannel。
2.1 requestLayout
requestLayout通过handler机制,会执行ViewRoot的relayoutWindow方法,通过AIDL接口最终会调用到WMS的relayoutWindow方法。需要注意的是:实际上WMS侧的addWindow比relayoutWindow先执行。
在relayoutWindow中调用了以下代码:
mInputMonitor.updateInputWindowsLw(true /*force*/);
/* Updates the cached window information provided to the input dispatcher. */ public void updateInputWindowsLw(boolean force) { if (!force && !mUpdateInputWindowsNeeded) { return; } mUpdateInputWindowsNeeded = false; if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw"); // Populate the input window list with information about all of the windows that // could potentially receive input. // As an optimization, we could try to prune the list of windows but this turns // out to be difficult because only the native code knows for sure which window // currently has touch focus. final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground; final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer; boolean addedUniverse = false; // If there's a drag in flight, provide a pseudowindow to catch drag input final boolean inDrag = (mService.mDragState != null); if (inDrag) { if (WindowManagerService.DEBUG_DRAG) { Log.d(WindowManagerService.TAG, "Inserting drag window"); } final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle; if (dragWindowHandle != null) { addInputWindowHandleLw(dragWindowHandle); } else { Slog.w(WindowManagerService.TAG, "Drag is in progress but there is no " + "drag window handle."); } } final int NFW = mService.mFakeWindows.size(); for (int i = 0; i < NFW; i++) { addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle); } // Add all windows on the default display. final int numDisplays = mService.mDisplayContents.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { final WindowState child = windows.get(winNdx); final InputChannel inputChannel = child.mInputChannel; final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; if (inputChannel == null || inputWindowHandle == null || child.mRemoved) { // Skip this window because it cannot possibly receive input. continue; } final int flags = child.mAttrs.flags; final int privateFlags = child.mAttrs.privateFlags; final int type = child.mAttrs.type; final boolean hasFocus = (child == mInputFocus); final boolean isVisible = child.isVisibleLw(); final boolean hasWallpaper = (child == mService.mWallpaperTarget) && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY); // If there's a drag in progress and 'child' is a potential drop target, // make sure it's been told about the drag if (inDrag && isVisible && onDefaultDisplay) { mService.mDragState.sendDragStartedIfNeededLw(child); } if (universeBackground != null && !addedUniverse && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) { final WindowState u = universeBackground.mWin; if (u.mInputChannel != null && u.mInputWindowHandle != null) { addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags, u.mAttrs.privateFlags, u.mAttrs.type, true, u == mInputFocus, false); } addedUniverse = true; } if (child.mWinAnimator != universeBackground) { addInputWindowHandleLw(inputWindowHandle, child, flags, privateFlags, type, isVisible, hasFocus, hasWallpaper); } } } // Send windows to native code. mService.mInputManager.setInputWindows(mInputWindowHandles); // Clear the list in preparation for the next round. clearInputWindowHandlesLw(); if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw"); }
这个函数将当前系统中带有InputChannel的Activity窗口都设置为InputManager的输入窗口。这些窗口保存在mInputWindowHandles数组中。
InputManagerService.java
public void setInputWindows(InputWindowHandle[] windowHandles) { nativeSetInputWindows(mPtr, windowHandles); }
接下来会调用native的setInputWindows方法。
com_android_server_Input_inputManagerService.cpp
static void nativeSetInputWindows(JNIEnv* env, jclass clazz, jint ptr, jobjectArray windowHandleObjArray) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); im->setInputWindows(env, windowHandleObjArray); }
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) { Vector<sp<InputWindowHandle> > windowHandles; if (windowHandleObjArray) { jsize length = env->GetArrayLength(windowHandleObjArray); for (jsize i = 0; i < length; i++) { jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); if (! windowHandleObj) { break; // found null element indicating end of used portion of the array } sp<InputWindowHandle> windowHandle = android_server_InputWindowHandle_getHandle(env, windowHandleObj); if (windowHandle != NULL) { windowHandles.push(windowHandle); } env->DeleteLocalRef(windowHandleObj); } } mInputManager->getDispatcher()->setInputWindows(windowHandles); // Do this after the dispatcher has updated the window handle state. bool newPointerGesturesEnabled = true; size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { newPointerGesturesEnabled = false; } } uint32_t changes = 0; { // acquire lock AutoMutex _l(mLock); if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; changes |= InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT; } } // release lock if (changes) { mInputManager->getReader()->requestRefreshConfiguration(changes); } }
这个函数首先将Java层的Window转换成C++层的windowHandle,然后放在windowHandles向量中,最后将这些输入窗口设置到InputDispatcher中去。
InputDispatcher.cpp
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { #if DEBUG_FOCUS ALOGD("setInputWindows"); #endif { // acquire lock AutoMutex _l(mLock); Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles; mWindowHandles = inputWindowHandles; sp<InputWindowHandle> newFocusedWindowHandle; bool foundHoveredWindow = false; for (size_t i = 0; i < mWindowHandles.size(); i++) { const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) { mWindowHandles.removeAt(i--); continue; } if (windowHandle->getInfo()->hasFocus) { newFocusedWindowHandle = windowHandle; } if (windowHandle == mLastHoverWindowHandle) { foundHoveredWindow = true; } } if (!foundHoveredWindow) { mLastHoverWindowHandle = NULL; } if (mFocusedWindowHandle != newFocusedWindowHandle) { if (mFocusedWindowHandle != NULL) { #if DEBUG_FOCUS ALOGD("Focus left window: %s", mFocusedWindowHandle->getName().string()); #endif sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel(); if (focusedInputChannel != NULL) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked( focusedInputChannel, options); } } if (newFocusedWindowHandle != NULL) { #if DEBUG_FOCUS ALOGD("Focus entered window: %s", newFocusedWindowHandle->getName().string()); #endif } mFocusedWindowHandle = newFocusedWindowHandle; } for (size_t i = 0; i < mTouchState.windows.size(); i++) { TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { #if DEBUG_FOCUS ALOGD("Touched window was removed: %s", touchedWindow.windowHandle->getName().string()); #endif sp<InputChannel> touchedInputChannel = touchedWindow.windowHandle->getInputChannel(); if (touchedInputChannel != NULL) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked( touchedInputChannel, options); } mTouchState.windows.removeAt(i--); } } // Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (size_t i = 0; i < oldWindowHandles.size(); i++) { const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i); if (!hasWindowHandleLocked(oldWindowHandle)) { #if DEBUG_FOCUS ALOGD("Window went away: %s", oldWindowHandle->getName().string()); #endif oldWindowHandle->releaseInfo(); } } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); }
这个方法的主要作用就是从传进来的窗口向量中,找到哪一个窗口是获得焦点的,获得焦点的输入窗口即为当前激活的窗口,并把窗口的句柄保存在mFocusedWindowHandle中。它的定义如下:
// Focus tracking for keys, trackball, etc.
sp<InputWindowHandle> mFocusedWindowHandle;
2.2 WMS注册InputChannel(mWindowSession.addToDisplay)
这个函数最终回调用到WMS的addWindow方法,和通道建立相关的主要代码如下: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); }
这里的outInputChannel是在setView时new并传入到addWindow中的。win是WindowState对象,运行在WMS侧。
通过InputChannel.openInputChannelPair函数来创建一对输入通道,其中一个位于WindowManagerService中,另外一个通过outInputChannel参数返回到应用程序中:
public void transferTo(InputChannel outParameter) { if (outParameter == null) { throw new IllegalArgumentException("outParameter must not be null"); } nativeTransferTo(outParameter); }
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, jobject otherObj) { 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); }
接下来我们就看一下InputChannel.openInputChannelPair函数的实现。
public static InputChannel[] openInputChannelPair(String name) { if (name == null) { throw new IllegalArgumentException("name must not be null"); } if (DEBUG) { Slog.d(TAG, "Opening input channel pair '" + name + "'"); } return nativeOpenInputChannelPair(name); }
native实现
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, jclass clazz, jstring nameObj) { const char* nameChars = env->GetStringUTFChars(nameObj, NULL); String8 name(nameChars); env->ReleaseStringUTFChars(nameObj, nameChars); sp<InputChannel> serverChannel; sp<InputChannel> clientChannel; status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { String8 message; message.appendFormat("Could not open input channel pair. status=%d", result); jniThrowRuntimeException(env, message.string()); return NULL; } jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); if (env->ExceptionCheck()) { return NULL; } jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(serverChannel)); if (env->ExceptionCheck()) { return NULL; } jobject clientChannelObj = 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; }
这个函数根据传进来的参数name在C++层分别创建两个InputChannel,一个作为Server端使用,一个作为Client端使用,这里的Server端即是指InputManager,而Client端即是指应用程序。这两个本地的InputChannel是通过InputChannel::openInputChannelPair函数创建的,创建完成后,再相应地在Java层创建相应的两个InputChannel,然后返回。
openInputChannelPair的实现如下:
status_t InputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -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)); String8 serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; }
先看一下InputChannel的构造函数
InputChannel::InputChannel(const String8& name, int fd) : mName(name), mFd(fd) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.string(), fd); #endif int result = fcntl(mFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " "non-blocking. errno=%d", mName.string(), errno); }每个channel都有一个文件描述符,这个文件描述符就是创建的socket。
通过调用Linux的socketpair()方法建立一对匿名的已经连接的套接字,然后调用setsockopt()方法为其分配内存,然后用其做为参数,创建native环境中的InputChannel对象,分别赋值给outServerChannel、和outClientChannel指针。
创建好两个native层的InputChannel对象后,存在channelPair数组中,然后通过JNI返回到JAVA环境中。
下一步,需要把刚才创建的Server端的输入通道注册到InputManager中:
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
public void registerInputChannel(InputChannel inputChannel, InputWindowHandle inputWindowHandle) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null."); } nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false); }
native的实现
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz, jint ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) { 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_t status = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor); if (status) { String8 message; 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); } }
status_t NativeInputManager::registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { return mInputManager->getDispatcher()->registerInputChannel( inputChannel, inputWindowHandle, monitor); }
这个函数主要是调用了InputDispatcher的registerInputChannel来真正执行注册输入通道的操作。
status_t InputDispatcher::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; }这个函数首先会通过getConnectionIndexLocked检查从参数传进来的InputChannel是否已经注册过了,如果已经注册过了,就返回一个BAD_VALUE值了,否则的话,就会创建一个Connection对象来封装即将要注册的inputChannel,并添加到mConnectionsByFd中,如果monitor为true,也把这个inputChannel添加到mMonitoringChannels中。
当需要通过管道发送消息的时候,从该列表中取出Connection对象,该对象与客户端是相对应的,之后调用mLooper->addFd()方法,把InputChannel对应的描述符添加到mLooper内部的描述符列表中。这里完成了wms的InputChannel的注册,即serverChannel。
在Looper类内部,会创建一个管道,然后Looper会睡眠在这个管道的读端,等待另外一个线程来往这个管道的写端写入新的内容,从而唤醒等待在这个管道读端的线程,除此之外,Looper还可以同时睡眠等待在其它的文件描述符上,因为它是通过Linux系统的epoll机制来批量等待指定的文件有新的内容可读的。这些其它的文件描述符就是通过Looper类的addFd成函数添加进去的了,在添加的时候,还可以指定回调函数,即当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个hanldeReceiveCallback函数。
至此,Server端的InputChannel就注册完成了。
2.3 应用程序进程注册InputChannel
if (view instanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());这里一般会执行最后的
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());接着,执行构造函数
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); } ......
接着调用父类的构造函数
public InputEventReceiver(InputChannel inputChannel, Looper looper) { 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"); }
JNI层
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { 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_t status = receiver->initialize(); if (status) { String8 message; 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<jint>(receiver.get()); }创建NativeInputEventReceiver
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false), mFdEvents(0) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); #endif }
执行初始化函数
status_t NativeInputEventReceiver::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); } } }其实,执行的最终过程向Looper中加入文件描述符,只是这里的Looper是应用程序进程的Looper。
服务端和客户端都注册了Channel,最终都向Looper中加入了文件描述符。服务端使用的Channel是InputDispatcher创建的时候生成的,这里没有Java Looper,只有native Looper。
而客户端的Looper有一个Java的Looper和一个native Looper。
JAVA Looper包含一个MessageQueue,MessageQueue对应的Native 实例是一个NativeMessageQueue实例,NativeMessageQueue在创建的时候生成一个Native Looper。
客户端UI主线程循环读取Java Looper的消息以进行下一步的处理。
其实Native Looper存在的意义就是作为JAVA Looper机制的开关器,
1. 当消息队列中有消息存入时,唤醒Natvice Looper,至于如何发送向线程消息,这就用到了Handler;
2. 当消息队列中没有消息时或者消息尚未到处理时间时,Natvice Looper block住整个线程。
也就是说:创建了JAVA Looper的线程只有在有消息待处理时才处于活跃状态,无消息时block在等待消息写入的状态。Looper相关的内容可以参考 Android消息处理机制(Handler、Looper、MessageQueue与Message)
在以上客户端(ViewRoot)注册的时候,其实没有使用Handler向java的消息队列中push消息,而只是复用了native Looper。具体的过程如下:
UI主线程运行在Looper.loop();
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } }
以上通过queue.next()取得消息队列中的内容
Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { ...... if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } ...... } }
接着调用nativePollOnce
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis); }
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) { mInCallback = true; mLooper->pollOnce(timeoutMillis); mInCallback = false; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); mExceptionObj = NULL; }
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); int ident = response.request.ident; if (ident >= 0) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - returning signalled identifier %d: " "fd=%d, events=0x%x, data=%p", this, ident, fd, events, data); #endif if (outFd != NULL) *outFd = fd; if (outEvents != NULL) *outEvents = events; if (outData != NULL) *outData = data; return ident; } } if (result != 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - returning result %d", this, result); #endif if (outFd != NULL) *outFd = 0; if (outEvents != NULL) *outEvents = 0; if (outData != NULL) *outData = NULL; return result; } result = pollInner(timeoutMillis); }
int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif // Adjust the timeout based on when the next message is due. if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime); if (messageTimeoutMillis >= 0 && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) { timeoutMillis = messageTimeoutMillis; } #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } // Poll. int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; // We are about to idle. mIdling = true; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // No longer idling. mIdling = false; // Acquire lock. mLock.lock(); // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } ALOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - timeout", this); #endif result = ALOOPER_POLL_TIMEOUT; goto Done; } // Handle all events. #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } } } Done: ; // Invoke pending message callbacks. mNextMessageUptime = LLONG_MAX; while (mMessageEnvelopes.size() != 0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); if (messageEnvelope.uptime <= now) { // Remove the envelope from the list. // We keep a strong reference to the handler until the call to handleMessage // finishes. Then we drop it so that the handler can be deleted *before* // we reacquire our lock. { // obtain handler sp<MessageHandler> handler = messageEnvelope.handler; Message message = messageEnvelope.message; mMessageEnvelopes.removeAt(0); mSendingMessage = true; mLock.unlock(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", this, handler.get(), message.what); #endif handler->handleMessage(message); } // release handler mLock.lock(); mSendingMessage = false; result = ALOOPER_POLL_CALLBACK; } else { // The last message left at the head of the queue determines the next wakeup time. mNextMessageUptime = messageEnvelope.uptime; break; } } // Release lock. mLock.unlock(); // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); if (response.request.ident == ALOOPER_POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd); } // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll. response.request.callback.clear(); result = ALOOPER_POLL_CALLBACK; } } return result; }
这里借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。epoll机制这里不多介绍了。
这里的int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);就是等待文件描述符指向的文件的内容发生变化。
这些文件描述符是通过Looper.addFd添加进去的。如果制定了回调函数,这样当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个回调函数。
这里再介绍一下Looper.addFd。
int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data); }
创建SimpleLooperCallback时,会把这个函数指针赋值给它的成员mCallback,这个类有一个成员函数handleEvent,调用handleEvent就相当于调用注册进来的CallBack。
最后都是通过以下形式把对象作为参数传递给addFd的。WMS注册Channel的场景传递的是CallBack是函数指针,所以需要new一个SimpleLooperCallback.
应用程序进程注册Channel的场景直接传递的就是继承了LooperCallback的NativeInputEventReceiver对象,它实现了handleEvent方法。
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 = ALOOPER_POLL_CALLBACK; } int epollEvents = 0; if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; { // acquire lock AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; request.callback = callback; request.data = data; struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = epollEvents; eventItem.data.fd = fd; ssize_t requestIndex = 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; }
实际上是把callback赋值给了request.callback,并保存在mRequests中。
在 Looper::pollInner函数中,当有文件描述符指向的文件发生变化时,也就是说,从epoll_wait时,最终会调用response.request.callback->handleEvent(fd, events, data);
对于WMS注册Channel的场景,就会调用之前addFd中的callback,即InputDispatcher::handleReceiveCallback方法。
对于应用程序注册Channel的场景,就会调用NativeInputEventReceiver::handleEvent方法。
关于这两个方法的实现,之后再做介绍。
下一步,先分析Input子系统的事件处理过程。
4. Input子系统的事件处理流程
在Input子系统的初始化过程,启动了两个线程InputReaderThread和InputDispatcherThread,并进入到它们的线程执行函数threadLoop中。当没有事件发生时,InputReader等待底层的事件输入,InputDispatcher等待InputReder的通知,都在睡眠。一旦有事件产生,整个过程就动起来了。下面我们分析这个过程。分几个部分介绍:
1. InputReader处理
2. InputDispatcher处理
4.1 InputReader处理
先看线程的处理函数bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }
void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; bool inputDevicesChanged = false; Vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex _l(mLock); oldGeneration = mGeneration; timeoutMillis = -1; uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; timeoutMillis = 0; refreshConfigurationLocked(changes); } else if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } if (mNextTimeout != LLONG_MAX) { nsecs_t now = 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(); }
nputReaderThread线程会不民地循环调用InputReader.pollOnce函数来读入事件,而实际的事件读入操作是由EventHub.getEvent函数来进行的。如果当前没有事件发生,InputReaderThread线程就会睡眠在EventHub.getEvent函数上,而当事件发生后,就会执行processEventsLocked函数进一步处理。
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count;) { int32_t type = rawEvent->type; size_t batchSize = 1; if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { int32_t deviceId = rawEvent->deviceId; while (batchSize < count) { if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT || rawEvent[batchSize].deviceId != deviceId) { break; } batchSize += 1; } #if DEBUG_RAW_EVENTS ALOGD("BatchSize: %d Count: %d", batchSize, count); #endif processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: removeDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::FINISHED_DEVICE_SCAN: handleConfigurationChangedLocked(rawEvent->when); break; default: ALOG_ASSERT(false); // can't happen break; } } count -= batchSize; rawEvent += batchSize; } }
1.当EventHub尚未打开input系统eventXX设备时,InputReader去向EventHub获取事件时,EventHub会首先去打开所有的设备,并将每个设备信息以RawEvent的形式返给InputReader,也就是processEventsLocked()中处理的EventHubInterface::DEVICE_ADDED类型,该过程会根据每个设备的deviceId去创建InputDevice,并根据设备的classes来创建对应的InputMapper。
2.当所有的设备均被打开之后,InputReader去向EventHub获取事件时,EventHub回去轮询event节点,如果有事件,则会调用processEventsForDeviceLocked进行下一步的处理。
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { ssize_t deviceIndex = 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); }
void InputDevice::process(const RawEvent* rawEvents, size_t count) { // Process all of the events in order for each mapper. // We cannot simply ask each mapper to process them in bulk because mappers may // have side-effects that must be interleaved. For example, joystick movement events and // gamepad button presses are handled by different mappers but they should be dispatched // in the order received. size_t numMappers = mMappers.size(); for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { #if DEBUG_RAW_EVENTS ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld", rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when); #endif if (mDropUntilNextSync) { if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { mDropUntilNextSync = false; #if DEBUG_RAW_EVENTS ALOGD("Recovered from input event buffer overrun."); #endif } else { #if DEBUG_RAW_EVENTS ALOGD("Dropped input event while waiting for next input sync."); #endif } } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { ALOGI("Detected input event buffer overrun for device %s.", getName().string()); mDropUntilNextSync = true; reset(rawEvent->when); } else { for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); } } } }
这里主要是调用 mapper->process,mapper相关的信息保存在mMappers数组中,InputReader会利用它来管理所收到的 input event,这些mapper 有 SwitchInputMapper,
VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper等等。
这些mapper是什么时候创建并添加到数组中的?主要是在DEVICE_ADDED时添加进去的。调用过程大致如下:
InputReader.cpp
processEventsLocked
-》addDeviceLocked
-》createDeviceLocked
-》 device = new InputDevice
device->addMapper
具体过程不做细致的分析了,总之,InputReader维护了mDevices和mMappers,到有事件发生时,InputReader会遍历所有的mapper,并调用对应的process方法。
本文主要讲解hard key(比如power,home,back,menu, volume up down等),对应的mapper是KeyboardInputMapper。所以事件到来时,会调用KeyboardInputMapper的process方法。
void KeyboardInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_KEY: { int32_t scanCode = rawEvent->code; int32_t usageCode = mCurrentHidUsage; mCurrentHidUsage = 0; if (isKeyboardOrGamepadKey(scanCode)) { int32_t keyCode; uint32_t flags; if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) { keyCode = AKEYCODE_UNKNOWN; flags = 0; } processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags); } break; } case EV_MSC: { if (rawEvent->code == MSC_SCAN) { mCurrentHidUsage = rawEvent->value; } break; } case EV_SYN: { if (rawEvent->code == SYN_REPORT) { mCurrentHidUsage = 0; } } }
这里的主要处理语句是processKey。
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) { if (down) { // Rotate key codes according to orientation if needed. if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { keyCode = rotateKeyCode(keyCode, mOrientation); } // Add key down. ssize_t keyDownIndex = findKeyDown(scanCode); if (keyDownIndex >= 0) { // key repeat, be sure to use same keycode as before in case of rotation keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode; } else { // key down if ((policyFlags & POLICY_FLAG_VIRTUAL) && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { return; } mKeyDowns.push(); KeyDown& keyDown = mKeyDowns.editTop(); keyDown.keyCode = keyCode; keyDown.scanCode = scanCode; } mDownTime = when; } else { // Remove key down. ssize_t keyDownIndex = findKeyDown(scanCode); if (keyDownIndex >= 0) { // key up, be sure to use same keycode as before in case of rotation keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode; mKeyDowns.removeAt(size_t(keyDownIndex)); } else { // key was not actually down ALOGI("Dropping key up from device %s because the key was not down. " "keyCode=%d, scanCode=%d", getDeviceName().string(), keyCode, scanCode); return; } } int32_t oldMetaState = mMetaState; int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState); bool metaStateChanged = oldMetaState != newMetaState; if (metaStateChanged) { mMetaState = newMetaState; updateLedState(false); } nsecs_t downTime = 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 | POLICY_FLAG_WAKE_DROPPED))) { policyFlags |= POLICY_FLAG_WAKE_DROPPED; } if (metaStateChanged) { getContext()->updateGlobalMetaState(); } if (down && !isMetaKey(keyCode)) { getContext()->fadePointer(); } NotifyKeyArgs args(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); }
这个函数首先对对按键作一些处理,例如,当某一个DPAD键被按下时,根据当时屏幕方向的不同,它所表示的意义也不同,因此,这里需要根据当时屏幕的方向来调整键盘码,如果这个键是一直按着不放的,不管屏幕的方向如何,必须保证后面的键盘码和前面的一样,如果是第一次按下某个键,还必须把它保存在mLocked.keyDowns里面,就是为了处理上面讲的当这个键盘一直按着不放的时候屏幕方向发生改变的情况。如果是松开键盘上的某个键,就把它从mLocked.keyDowns里面删除。
最后,调用getListener()->notifyKey通知InputDispatcher,有key事件发生了。
getListener()->notifyKey如何通知InputDispatcher?下面分析这一过程。
InputManager在构造的时候会创建InputReader。
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
此时,mDispatcher的作为参数传递给Reader
mQueuedListener = new QueuedInputListener(listener);mQueuedListener有一个成员mInnerListener指向dispatcher。
再来看一下NotifyArgs,它用来描述输入事件,根据不同的输入事件类型,也存在几个派生的NotifyArgs,比如NotifyKeyArgs,NotifyMotionArgs,NotifySwitchArgs等等,这里使用的是NotifyKeyArgs。
再来看一下getListener的实现。
InputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); }由于mQueuedListener是sp,所以使用了get方法,这里不做介绍了,作用就是取得QueuedInputListener实例。
接着调用QueuedInputListener的notifyKey,传入的参数是创建的NotifyKeyArgs的地址。
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { mArgsQueue.push(new NotifyKeyArgs(*args)); }
这里重新构造了一个NotifyKeyArgs,并push到QueuedInputListener的成员mArgsQueue向量里。
这个过程执行完之后,返回到InputReader::loopOnce的最后一条语句mQueuedListener->flush();
void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; bool inputDevicesChanged = false; Vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex _l(mLock); oldGeneration = mGeneration; timeoutMillis = -1; uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; timeoutMillis = 0; refreshConfigurationLocked(changes); } else if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } } // release lock size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } ...... // 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(); }
void QueuedInputListener::flush() { size_t count = mArgsQueue.size(); for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; args->notify(mInnerListener); delete args; } mArgsQueue.clear(); }这里有个循环,用来取得QueuedInputListener的mArgsQueue里的事件描述参数(这时之前push的),然后逐个调用NotifyArgs各个派生对象的notify方法。注意这里的参数是mInnerListener,其实指向dispatcher。
Key Event会调用NotifyKeyArgs::notify
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyKey(this); }
这里就会调用InputDispatcher的notifykey,参数是NotifyKeyArgs。
这样就把事件通知给了dispathcer。
下一步分析dispatcher的处理流程。
4.2 InputDispatcher处理
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_t policyFlags = args->policyFlags; int32_t flags = args->flags; int32_t metaState = 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_ALT) { metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON; } if (policyFlags & POLICY_FLAG_ALT_GR) { metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON; } if (policyFlags & POLICY_FLAG_SHIFT) { metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON; } if (policyFlags & POLICY_FLAG_CAPS_LOCK) { metaState |= AMETA_CAPS_LOCK_ON; } if (policyFlags & POLICY_FLAG_FUNCTION) { metaState |= AMETA_FUNCTION_ON; } policyFlags |= POLICY_FLAG_TRUSTED; KeyEvent event; event.initialize(args->deviceId, args->source, args->action, flags, args->keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime); mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); if (policyFlags & POLICY_FLAG_WOKE_HERE) { flags |= AKEY_EVENT_FLAG_WOKE_HERE; } 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_t repeatCount = 0; KeyEntry* newEntry = new KeyEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, flags, args->keyCode, args->scanCode, metaState, repeatCount, args->downTime); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); } // release lock if (needWake) { mLooper->wake(); } }
函数首先是调用validateKeyEvent函数来验证action参数是否正确。正确的action参数的值只能为AKEY_EVENT_ACTION_DOWN(按下)或者AKEY_EVENT_ACTION_UP(松开)。
另外,InputDispatcher会截取这个按键事件,根据当前设备的状况来优先消化这个事件,这个过程当然是在将事件dispatch给ViewRoot之前。最终该过程交由interceptKeyBeforeQueueing()@PhoneWindowManager.java来处理。interceptKeyBeforeQueueing()主要是对一些特殊案件的特殊处理,并判断该按键是够应该传递给ViewRoot。通过设置标志位policyFlags的值来判断是否给ViewRoot,例如policyFlags&POLICY_FLAG_PASS_TO_USER
== 1 则应该传递给ViewRoot。
interceptKeyBeforeQueueing()特殊处理主要是针对在锁屏或者屏幕不亮的情况的下收到特殊的键值,如音量键或者wake键。wake键是指能够点亮屏幕的键时的操作。
下面再分析一下mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);是如何调用到interceptKeyBeforeQueueing()@PhoneWindowManager.java。
这里的mPolicy实际上是NativeInputManager的引用,是在NativeInputManager构造过程中创建InputManager时作为参数最终传递给InputDispatcher的成员mPolicy的。
所以执行下面的过程:
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) { // Policy: // - Ignore untrusted events and pass them along. // - Ask the window manager what to do with normal events and trusted injected events. // - For normal events wake and brighten the screen if currently off or dim. if ((policyFlags & POLICY_FLAG_TRUSTED)) { nsecs_t when = keyEvent->getEventTime(); bool isScreenOn = this->isScreenOn(); bool isScreenBright = this->isScreenBright(); JNIEnv* env = jniEnv(); jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); jint wmActions; if (keyEventObj) { wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeQueueing, keyEventObj, policyFlags, isScreenOn); if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { wmActions = 0; } android_view_KeyEvent_recycle(env, keyEventObj); env->DeleteLocalRef(keyEventObj); } else { ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing."); wmActions = 0; } if (!(policyFlags & POLICY_FLAG_INJECTED)) { if (!isScreenOn) { policyFlags |= POLICY_FLAG_WOKE_HERE; } if (!isScreenBright) { policyFlags |= POLICY_FLAG_BRIGHT_HERE; } } handleInterceptActions(wmActions, when, /*byref*/ policyFlags); } else { policyFlags |= POLICY_FLAG_PASS_TO_USER; } }主要处理是如下代码:
wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeQueueing, keyEventObj, policyFlags, isScreenOn);这里的mServiceObj实际上是InputManagerService。调用如下方法。
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { return mWindowManagerCallbacks.interceptKeyBeforeQueueing( event, policyFlags, isScreenOn); }
mWindowManagerCallbacks实际是一个InputMonitor对象,是在开机是有SystemServer调用的,
inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start()所以i接着执行InputMonitor的nterceptKeyBeforeQueueing方法
public int interceptKeyBeforeQueueing( KeyEvent event, int policyFlags, boolean isScreenOn) { return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); }这里的mService就是WindowManagerService,mPolicy就是PhoneWindowManager。
下面再回到InputDispatcher::notifyKey的处理方法继续分析。
if (shouldSendKeyToInputFilterLocked(args)) { mLock.unlock(); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { return; // event was consumed by the filter } mLock.lock(); }
接下来,按键马上进入第二轮处理。如果用户在Setting->Accessibility 中选择打开某些功能,比如说手势识别,Android的AccessbilityManagerService(辅助功能服务) 会创建一个 InputFilter 对象,它会检查输入的事件,根据需要可能会转换成新的Event,比如说两根手指头捏动的手势最终会变成ZOOM的event. 目前,InputManagerService 只支持一个InputFilter, 新注册的InputFilter会把老的覆盖。InputFilter
运行在SystemServer 的 ServerThread 线程里(除了绘制,窗口管理和Binder调用外,大部分的System Service 都运行在这个线程里)。而filterInput() 的调用是发生在Input Reader线程里,通过InputManagerService 里的 InputFilterHost 对象通知另外一个线程里的InputFilter 开始真正的解析工作。所以,InputReader 线程从这里结束一轮的工作,重新进入epoll_wait() 等待新的用户输入。InputFilter
的工作也分为两个步骤,首先由InputEventConsistencyVerifier 对象(InputEventConsistencyVerifier.java)对输入事件的完整性做一个检查,检查事件的ACTION_DOWN 和 ACTION_UP 是否一一配对。如果在Android Logcat 里看到过以下一些类似的打印:"ACTION_UP but key was not down." 就出自此处。接下来,进入到AccessibilityInputFilter 的 onInputEvent(),这里将把输入事件(主要是MotionEvent)进行处理,根据需要变成另外一个Event,然后通过sendInputEvent()将事件发回给InputDispatcher。最终调用到injectInputEvent()
将这个事件送入 mInBoundQueue。
其实,事件的过滤和拦截采用了策略模式机制进行不同的事件处理,由类图中的InputManager、InputFilter、PhoneWindowManager、Callbacks、InputMonitor等JAVA对象共同完成事件的过滤和拦截;Callbacks、InputMonitor主要负责回调事件的转发,InputFilter负责事件过滤请求的处理;应用可以通过InputManager对象的injectInputEvent函数完成事件的插入;PhoneWindowManager对象负责派发事件的拦截。事件的过滤请求和拦截请求流程相似,只是目的地不一样。
然后,将事件存储在一个输入队列mInboundQueue中:
KeyEntry* newEntry = new KeyEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, flags, args->keyCode, args->scanCode, metaState, repeatCount, args->downTime); needWake = enqueueInboundEventLocked(newEntry);
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { 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; } case EventEntry::TYPE_MOTION: { // Optimize case where the current application is unresponsive and the user // decides to touch a window in a different application. // If the application takes too long to catch up then we drop all events preceding // the touch into the other window. MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && mInputTargetWaitApplicationHandle != NULL) { int32_t displayId = motionEntry->displayId; int32_t x = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_Y)); sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); if (touchedWindowHandle != NULL && touchedWindowHandle->inputApplicationHandle != mInputTargetWaitApplicationHandle) { // User touched a different application than the one we are waiting on. // Flag the event, and start pruning the input queue. mNextUnblockedEvent = motionEntry; needWake = true; } } break; } } return needWake;
最后,唤醒InputDispatccherThread线程
if (needWake) { mLooper->wake(); }其实,就是想管道中写入一个W。
void Looper::wake() { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ wake", this); #endif ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); } while (nWrite == -1 && errno == EINTR); if (nWrite != 1) { if (errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } } }
下面分析InputDispatcherThread的处理流程。
bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; }
void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = 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_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis);
这里主要处理是
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); // Reset the key repeat timer whenever we disallow key events, even if the next event // is not a key. This is to ensure that we abort a key repeat if the device is just coming // out of sleep. if (!mPolicy->isKeyRepeatEnabled()) { resetKeyRepeatLocked(); } // If dispatching is frozen, do not process timeouts or try to deliver any new events. if (mDispatchFrozen) { #if DEBUG_FOCUS ALOGD("Dispatch frozen. Waiting some more."); #endif return; } // Optimize latency of app switches. // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has // been pressed. When it expires, we preempt dispatch and drop all other pending events. bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; if (mAppSwitchDueTime < *nextWakeupTime) { *nextWakeupTime = mAppSwitchDueTime; } // Ready to start a new event. // If we don't already have a pending event, go grab one. 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(); } // Now we have an event to dispatch. // All events are eventually dequeued and processed this way, even if we intend to drop them. ALOG_ASSERT(mPendingEvent != NULL); bool done = false; DropReason dropReason = DROP_REASON_NOT_DROPPED; if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { dropReason = DROP_REASON_POLICY; } else if (!mDispatchEnabled) { dropReason = DROP_REASON_DISABLED; } if (mNextUnblockedEvent == mPendingEvent) { mNextUnblockedEvent = NULL; } switch (mPendingEvent->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: { ...... break; } case EventEntry::TYPE_DEVICE_RESET: { ...... break; } case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); if (isAppSwitchDue) { if (isAppSwitchKeyEventLocked(typedEntry)) { resetPendingAppSwitchLocked(true); isAppSwitchDue = false; } else if (dropReason == DROP_REASON_NOT_DROPPED) { dropReason = DROP_REASON_APP_SWITCH; } } if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEventLocked(currentTime, typedEntry)) { dropReason = DROP_REASON_STALE; } if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { dropReason = DROP_REASON_BLOCKED; } done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } case EventEntry::TYPE_MOTION: { ...... default: ALOG_ASSERT(false); break; } if (done) { if (dropReason != DROP_REASON_NOT_DROPPED) { dropInboundEventLocked(mPendingEvent, dropReason); } releasePendingEventLocked(); *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } }
这里涉及到事件的丢弃,并不是所有的InputReader发送来的事件我们都需要传递给应用,比如上节讲到的翻盖/滑盖事件,除此之外的按键,触屏,轨迹球(后两者统一按motion事件处理),也会有部分的事件被丢弃,InputDispatcher总会根据一些规则来丢弃掉一部分事件,我们来简单分析一下哪些情况下我们需要丢弃掉部分事件?
InputDispatcher.h中定义了一个包含有丢弃原因的枚举:
enum DropReason { DROP_REASON_NOT_DROPPED = 0, DROP_REASON_POLICY = 1, DROP_REASON_APP_SWITCH = 2, DROP_REASON_DISABLED = 3, DROP_REASON_BLOCKED = 4, DROP_REASON_STALE = 5, };1. DROP_REASON_NOT_DROPPED
不需要丢弃
2. DROP_REASON_POLICY
设置为DROP_REASON_POLICY主要有两种情形:
A. 在InputReader notify InputDispatcher之前,Policy会判断不需要传递给应用的事件。
B. 在InputDispatcher dispatch事件前,PhoneWindowManager使用方法interceptKeyBeforeDispatching()提前consume掉一些按键事件。
interceptKeyBeforeDispatching()主要对HOME/MENU/SEARCH按键的特殊处理,如果此时能被consume掉,那么在InputDispatcher 中将被丢弃。
3.DROP_REASON_APP_SWITCH
当有App switch 按键如HOME/ENDCALL按键发生时,当InputReader向InputDispatcher 传递app switch按键时,会设置一个APP_SWITCH_TIMEOUT 0.5S的超时时间,当0.5s超时时,InputDispatcher 尚未dispatch到这个app switch按键时,InputDispatcher 将会丢弃掉mInboundQueue中所有处在app switch按键前的按键事件。这么做的目的是保证app switch按键能够确保被处理。此时被丢弃掉的按键会被置为DROP_REASON_APP_SWITCH。
4. DROP_REASON_DISABLED
这个标志表示当前的InputDispatcher 被disable掉了,不能dispatch任何事件,比如当系统休眠时或者正在关机时会用到。
5. DROP_REASON_BLOCKED
如果事件传递后,已经进入了另外的应用,就把以前的事件丢弃。
6.DROP_REASON_STALE
设置的超时是10秒。如果超过了10秒,事件没有处理掉,那么就直接丢弃。
我们主要分析事件传递到ViewRoot的过程接着往下分析。
主要的处理是dispatchKeyLocked。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. 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); } // Handle case where the policy asked us to try again later last time. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { if (entry->interceptKeyWakeupTime < *nextWakeupTime) { *nextWakeupTime = entry->interceptKeyWakeupTime; } return false; // wait until next wakeup } entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; entry->interceptKeyWakeupTime = 0; } // Give the policy a chance to intercept the key. 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; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { if (*dropReason == DROP_REASON_NOT_DROPPED) { *dropReason = DROP_REASON_POLICY; } } // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); return true; } // Identify targets. Vector<InputTarget> inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } setInjectionResultLocked(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } addMonitoringTargetsLocked(inputTargets); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; }这里面又涉及到一次拦截:
// Give the policy a chance to intercept the key. 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这里是把事件加入到command队列mCommandQueue(其实这个command队列经常会用到,这里只是其中一个场景),当再次回到InputDispatcher::dispatchOnce时,会调用runCommandsLockedInterruptible来执行拦截的处理函数。同样这里的拦截最终是由interceptKeyBeforeDispatching@PhoneWindowManager执行的。这部分不再具体分析了。
接下来会查找FocusedWindow,然后调用dispatchEventLocked。
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { #if DEBUG_DISPATCH_CYCLE ALOGD("dispatchEventToCurrentInputTargets"); #endif ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true pokeUserActivityLocked(eventEntry); for (size_t i = 0; i < inputTargets.size(); i++) { const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { #if DEBUG_FOCUS ALOGD("Dropping event delivery to target with channel '%s' because it " "is no longer registered with the input dispatcher.", inputTarget.inputChannel->getName().string()); #endif } } }pokeUserActivityLocked主要是电源管理相关的,这里不再详细介绍,主要处理函数prepareDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, scaleFactor=%f, " "pointerIds=0x%x", connection->getInputChannelName(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor, inputTarget->pointerIds.value); #endif // Skip this event if the connection status is not normal. // We don't want to enqueue additional outbound events if the connection is broken. if (connection->status != Connection::STATUS_NORMAL) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dropping event because the channel status is %s", connection->getInputChannelName(), connection->getStatusLabel()); #endif return; } // Split a motion event if needed. if (inputTarget->flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry); if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { MotionEntry* splitMotionEntry = splitMotionEvent( originalMotionEntry, inputTarget->pointerIds); if (!splitMotionEntry) { return; // split event was dropped } #if DEBUG_FOCUS ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName()); logOutboundMotionDetailsLocked(" ", splitMotionEntry); #endif enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); splitMotionEntry->release(); return; } } // Not splitting. Enqueue dispatch entries for the event as is. enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); }一般情况下会执行到enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, 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); }这里会记录之前的事件队列是否为空的标志(wasEmpty),并判断新的事件是否加入到事件队列中,当条件不成立时,说明当前这个Activity窗口正在处事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行事件处理的流程了。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName()); #endif while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.isEmpty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.head; dispatchEntry->deliveryTime = currentTime; // Publish the event. status_t status; 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; } case EventEntry::TYPE_MOTION: { MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); PointerCoords scaledCoords[MAX_POINTERS]; const PointerCoords* usingCoords = motionEntry->pointerCoords; // Set the X and Y offset depending on the input source. float xOffset, yOffset, scaleFactor; if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { scaleFactor = dispatchEntry->scaleFactor; xOffset = dispatchEntry->xOffset * scaleFactor; yOffset = dispatchEntry->yOffset * scaleFactor; if (scaleFactor != 1.0f) { for (size_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i] = motionEntry->pointerCoords[i]; scaledCoords[i].scale(scaleFactor); } usingCoords = scaledCoords; } } else { xOffset = 0.0f; yOffset = 0.0f; scaleFactor = 1.0f; // We don't want the dispatch target to know. if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { for (size_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i].clear(); } usingCoords = scaledCoords; } } // Publish the motion event. status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); break; } default: ALOG_ASSERT(false); return; } // Check the result. if (status) { if (status == WOULD_BLOCK) { if (connection->waitQueue.isEmpty()) { ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " "This is unexpected because the wait queue is empty, so the pipe " "should be empty and we shouldn't have any problems writing an " "event to it, status=%d", connection->getInputChannelName(), status); abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } else { // Pipe is full and we are waiting for the app to finish process some events // before sending more events to it. #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " "waiting for the application to catch up", connection->getInputChannelName()); #endif connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " "status=%d", connection->getInputChannelName(), status); abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } return; } // Re-enqueue the event on the wait queue. connection->outboundQueue.dequeue(dispatchEntry); traceOutboundQueueLengthLocked(connection); connection->waitQueue.enqueueAtTail(dispatchEntry); traceWaitQueueLengthLocked(connection); } }
从它的outboundQueue队列中取出当前需要处理的事件,然后调用 connection->inputPublisher.publishMotionEvent通知Channel的接收端。
status_t InputPublisher::publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { ....... InputMessage msg; msg.header.type = InputMessage::TYPE_KEY; msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; msg.body.key.scanCode = scanCode; msg.body.key.metaState = metaState; msg.body.key.repeatCount = repeatCount; msg.body.key.downTime = downTime; msg.body.key.eventTime = eventTime; return mChannel->sendMessage(&msg); }
InputTransport.cpp
status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; 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进行系统调用(加::是为了能够正确的调用系统中的send,避免该类中刚好也有一个 close 函数的情况),通过send发送了一个socket消息,那么一定会有另一个socket接收消息,接收消息的就是Channel的另一端——UI主线程。UI主线程一直阻塞在epoll_wait,是在Looper::pollInner中调用的。
在应用程序注册InputChannel一节介绍过,当有事件到来的时候会调用哪个NativeInputEventReceiver::handleEvent方法。
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { #if DEBUG_DISPATCH_CYCLE // This error typically occurs when the publisher has closed the input channel // as part of removing a window or finishing an IME session, in which case // the consumer will soon be disposed as well. ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. " "events=0x%x", getInputChannelName(), events); #endif return 0; // remove the callback } if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } if (events & ALOOPER_EVENT_OUTPUT) { for (size_t i = 0; i < mFinishQueue.size(); i++) { const Finish& finish = mFinishQueue.itemAt(i); status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled); if (status) { mFinishQueue.removeItemsAt(0, i); if (status == WOULD_BLOCK) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.", getInputChannelName(), i, mFinishQueue.size()); #endif return 1; // keep the callback, try again later } ALOGW("Failed to send finished signal on channel '%s'. status=%d", getInputChannelName(), status); if (status != DEAD_OBJECT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); String8 message; message.appendFormat("Failed to finish input event. status=%d", status); jniThrowRuntimeException(env, message.string()); mMessageQueue->raiseAndClearException(env, "finishInputEvent"); } return 0; // remove the callback } } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Sent %u queued finish events; none left.", getInputChannelName(), mFinishQueue.size()); #endif mFinishQueue.clear(); setFdEvents(ALOOPER_EVENT_INPUT); return 1; } ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", getInputChannelName(), events); return 1; }对于输入事件的场景,主要处理是consumeEvents。
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.", getInputChannelName(), consumeBatches ? "true" : "false", frameTime); #endif if (consumeBatches) { mBatchedInputEventPending = false; } ScopedLocalRef<jobject> receiverObj(env, NULL); bool skipCallbacks = false; for (;;) { uint32_t seq; InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); if (status) { if (status == WOULD_BLOCK) { if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) { // There is a pending batch. Come back later. if (!receiverObj.get()) { receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); if (!receiverObj.get()) { ALOGW("channel '%s' ~ Receiver object was finalized " "without being disposed.", getInputChannelName()); return DEAD_OBJECT; } } mBatchedInputEventPending = true; #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", getInputChannelName()); #endif env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); if (env->ExceptionCheck()) { ALOGE("Exception dispatching batched input events."); mBatchedInputEventPending = false; // try again later } } return OK; } ALOGE("channel '%s' ~ Failed to consume input event. status=%d", getInputChannelName(), status); return status; } assert(inputEvent); if (!skipCallbacks) { if (!receiverObj.get()) { receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); if (!receiverObj.get()) { ALOGW("channel '%s' ~ Receiver object was finalized " "without being disposed.", getInputChannelName()); return DEAD_OBJECT; } } jobject inputEventObj; switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); #endif inputEventObj = android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); break; case AINPUT_EVENT_TYPE_MOTION: #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); #endif inputEventObj = android_view_MotionEvent_obtainAsCopy(env, static_cast<MotionEvent*>(inputEvent)); break; default: assert(false); // InputConsumer should prevent this from ever happening inputEventObj = NULL; } if (inputEventObj) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); #endif env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); if (env->ExceptionCheck()) { ALOGE("Exception dispatching input event."); skipCallbacks = true; } env->DeleteLocalRef(inputEventObj); } else { ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); skipCallbacks = true; } } if (skipCallbacks) { mInputConsumer.sendFinishedSignal(seq, false); } } }这里处理大体分3处:
1.
status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent);这里调用最终mChannel->receiveMessage取得从channel另一端发送过来的事件。
2.
case AINPUT_EVENT_TYPE_KEY: #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); #endif inputEventObj = android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); break;从native Event转换成java event
3.
env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);调用java层对象WindowInputEventReceiver的dispatchInputEvent方法。(gInputEventReceiverClassInfo对应的对象是WindowInputEventReceiver,这时在应用程序进程注册InputChannel时调用本地方法nativeInit时注册进去的)。
WindowInputEventReceiver继承自InputEventReceiver
private void dispatchInputEvent(int seq, InputEvent event) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); }
public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); }
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys // are processed in the order they were received and we cannot trust that // the time stamp of injected events are monotonic. QueuedInputEvent last = mPendingInputEventTail; if (last == null) { mPendingInputEventHead = q; mPendingInputEventTail = q; } else { last.mNext = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1; Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }事件都是立即执行(processImmediately为true),接着进入到ViewRoot的事件处理流程,这里不再赘述。
以上分析的是从WMS到应用程序的过程。
下面简单分析一下从应用程序到WMS的过程。
当ViewRoot处理完事件后,都会调用finishInputEvent ,下面就从这个函数开发分析ViewRoot-》wms的过程。
private void finishInputEvent(QueuedInputEvent q) { if (q.mReceiver != null) { boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; q.mReceiver.finishInputEvent(q.mEvent, handled); } else { q.mEvent.recycleIfNeededAfterDispatch(); } recycleQueuedInputEvent(q); }
public final void finishInputEvent(InputEvent event, boolean handled) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to finish an input event but the input event " + "receiver has already been disposed."); } else { int index = mSeqMap.indexOfKey(event.getSequenceNumber()); if (index < 0) { Log.w(TAG, "Attempted to finish an input event that is not in progress."); } else { int seq = mSeqMap.valueAt(index); mSeqMap.removeAt(index); nativeFinishInputEvent(mReceiverPtr, seq, handled); } } event.recycleIfNeededAfterDispatch(); }
static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jint seq, jboolean handled) { sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); status_t status = receiver->finishInputEvent(seq, handled); if (status && status != DEAD_OBJECT) { String8 message; message.appendFormat("Failed to finish input event. status=%d", status); jniThrowRuntimeException(env, message.string()); } }
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); #endif status_t status = mInputConsumer.sendFinishedSignal(seq, handled); if (status) { if (status == WOULD_BLOCK) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Could not send finished signal immediately. " "Enqueued for later.", getInputChannelName()); #endif Finish finish; finish.seq = seq; finish.handled = handled; mFinishQueue.add(finish); if (mFinishQueue.size() == 1) { setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); } return OK; } ALOGW("Failed to send finished signal on channel '%s'. status=%d", getInputChannelName(), status); } return status; }
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", mChannel->getName().string(), seq, handled ? "true" : "false"); #endif if (!seq) { ALOGE("Attempted to send a finished signal with sequence number 0."); return BAD_VALUE; } // Send finished signals for the batch sequence chain first. size_t seqChainCount = mSeqChains.size(); if (seqChainCount) { uint32_t currentSeq = seq; uint32_t chainSeqs[seqChainCount]; size_t chainIndex = 0; for (size_t i = seqChainCount; i-- > 0; ) { const SeqChain& seqChain = mSeqChains.itemAt(i); if (seqChain.seq == currentSeq) { currentSeq = seqChain.chain; chainSeqs[chainIndex++] = currentSeq; mSeqChains.removeAt(i); } } status_t status = OK; while (!status && chainIndex-- > 0) { status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); } if (status) { // An error occurred so at least one signal was not sent, reconstruct the chain. do { SeqChain seqChain; seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; seqChain.chain = chainSeqs[chainIndex]; mSeqChains.push(seqChain); } while (chainIndex-- > 0); return status; } } // Send finished signal for the last message in the batch. return sendUnchainedFinishedSignal(seq, handled); }
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::TYPE_FINISHED; msg.body.finished.seq = seq; msg.body.finished.handled = handled; return mChannel->sendMessage(&msg); }应用程序处理完事件后,最终也是通过Channel的sendMessage发送一个处理完成的Socket消息来通知channel的另一端。
在WMS注册Input Channel一节提到过,当WMS收到Socket消息后会调用handleReceiveCallback。
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { InputDispatcher* d = static_cast<InputDispatcher*>(data); { // acquire lock AutoMutex _l(d->mLock); ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); if (connectionIndex < 0) { ALOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", fd, events); return 0; // remove the callback } bool notify; sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex); if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", connection->getInputChannelName(), events); return 1; } nsecs_t currentTime = now(); bool gotOne = false; status_t status; for (;;) { uint32_t seq; bool handled; status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); if (status) { break; } d->finishDispatchCycleLocked(currentTime, connection, seq, handled); gotOne = true; } if (gotOne) { d->runCommandsLockedInterruptible(); if (status == WOULD_BLOCK) { return 1; } } notify = status != DEAD_OBJECT || !connection->monitor; if (notify) { ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", connection->getInputChannelName(), status); } } else { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn // about them. notify = !connection->monitor; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); } } // Unregister the channel. d->unregisterInputChannelLocked(connection->inputChannel, notify); return 0; // remove the callback } // release lock }
最主要的处理是以下两句:
1. status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
调用mChannel->receiveMessage用于获取消息。
2. d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", connection->getInputChannelName(), seq, toString(handled)); #endif connection->inputPublisherBlocked = false; if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { return; } // Notify other system components and prepare to start the next dispatch cycle. onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); }
void InputDispatcher::onDispatchCycleFinishedLocked( nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; }
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; nsecs_t finishTime = commandEntry->eventTime; uint32_t seq = commandEntry->seq; bool handled = commandEntry->handled; // Handle post-event policy actions. DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); if (dispatchEntry) { nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { String8 msg; msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ", connection->getWindowName(), eventDuration * 0.000001f); dispatchEntry->eventEntry->appendDescription(msg); ALOGI("%s", msg.string()); } bool restartEvent; if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); restartEvent = afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, handled); } else { restartEvent = false; } // Dequeue the event and start the next cycle. // Note that because the lock might have been released, it is possible that the // contents of the wait queue to have been drained, so we need to double-check // a few things. if (dispatchEntry == connection->findWaitQueueEntry(seq)) { connection->waitQueue.dequeue(dispatchEntry); traceWaitQueueLengthLocked(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.enqueueAtHead(dispatchEntry); traceOutboundQueueLengthLocked(connection); } else { releaseDispatchEntryLocked(dispatchEntry); } } // Start the next dispatch cycle for this connection. startDispatchCycleLocked(now(), connection); }由此可知在 InputDispatcher 一收到InputConsumer
发送的 finish message之后, 就马上去呼叫startDispatchCycleLocked 函數去檢查 outboundQueue
里面还有沒有新的input event. 若有的话就放入InputMessage 共享內存, 然後通知 ViewRootImpl 去共享內存抓取新的 input
event. 若沒有新的 input event, 就不做事等待有新的input event进入 outboundQueue.
相关文章推荐
- android的frameworks层键盘事件处理流程分析
- Android中Preference的使用以及监听事件分析处理流程
- Android事件处理分析+Android事件处理 +Android输入事件流程
- android源码分析——事件输入流程MotionEvent事件处理流程
- Android View系统源码分析(一)——概述&触摸事件总体处理流程
- android的frameworks层键盘事件处理流程分析
- Android的frameworks层键盘事件处理流程分析
- Android 事件捕捉和处理流程分析
- Android的frameworks层键盘事件处理流程分析
- Android的frameworks层键盘事件处理流程分析
- android的frameworks层键盘事件处理流程分析
- Android事件处理分析+Android事件处理 +Android输入事件流程
- android的frameworks层键盘事件处理流程分析
- Android 事件捕捉和处理流程分析
- android的frameworks层键盘事件处理流程分析
- Android O Touch事件处理流程源码分析
- Android按键事件处理流程 -- 从事件被派发到View层次结构的根节点DecorView开始分析
- 转载 Android 事件捕捉和处理流程分析
- Android的frameworks层键盘事件处理流程分析
- android的frameworks层键盘事件处理流程分析