MT6572平台来看
一、输入子系统得到事件信息
输入子系统首先由systemserver启动:
inputManager = new InputManagerService(context, wmHandler);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
在InputManagerService中start方法会通过JNI调用,启动Native层的InputReaderThread,InputDispatcherThread线程,从而开始Input系统的运行。InputReaderThread主要是执行和InputReader相关的内容,主要是从EventHub中读取事件,预处理事件,然会是根据policy来处理此事件,最后发送一个消息到InputDispatcher中通知事件的产生。紧接着InputDispatcher会开始事件的分发,通过InputChannel把事件分发给WindowManager或者应用程序。说以一个事件的流程是从 Eventhub ---> InputReader ---> InputDispatcher ---> InputPublisher ---> InputChannel ---> InputConsumer ---> WindowManager or Application
先看InputManagerService的构造函数,代码如下:
public InputManagerService(Context context, Handler handler) {//这里的handler是WindowManagerService处理消息专用的线程,InputManagerService会把消息发送到这个线程中loop
this.mContext = context;
this.mHandler = new InputManagerHandler(handler.getLooper());//而和InputManagerService相关的消息的处理时在这个对象中完成的
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//通过JNI调用来启动native层的input系统,然后把返回值存放在mPtr中
}
从代码可以看出,InputManagerService的构造是很简单的,只是在最后通过JNI方法初始化了native层的Input系统。接下来我们就看看在native层都做了些什么,代码如下:
static jint nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp
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
}
在native层初始化的时候,创建了一个名叫NativeInputMnager的对象,这个对象主要负责和系统的其他模块交互,而且InputReader和InputDispatcher都是只运行在Native层中,如果需要调用Java函数也是通过这个对象进行的,另外他实现了InputReaderPolicyInterface和InputDispatcherPolicyInterface,是一个重要的Policy。NativeInputManager在构造过程中,完成了InputManager在native基本运行组件的创建,比如创建了EventHub对象,它是事件的Android系统的起源地,所有的事件都是它从驱动中读取出来的;还创建了InputReaderThread线程用来执行InputReader的功能;InputDispatcherThread用来执行InputDispatcher的功能;同时也创建了InputManager来管理EventHub,InputReader,InputReaderThread,InputDispatcher,InputDispatcherThread这些Native运行的基本对象。这些对象的创建过程中并没有非常重要的调用,这里略过代码。不过要注意一点的是NativeInputManager是InputReaderPolicyInterface和InputDispatcherPolicyInterface的子类,因此在构造InputReader和InputDispatcher的时候要用到NativieInputManager对象。
在对象构建完成后,开始执行start方法,让之前创建的这些对象运行起来。start方法通过JNI调用让native层的Input系统运行起来,然后在Java层把自己列入WatchDog的监视范围内。之后定义下自己需要接受的外部通知等。
在NativeInputManager@com_android_server_input_InputManagerService.cpp中会新建一下EventHub及InputManager。并把EventHub作为参数传入InputManager。这个类函数比较简单,只维护了几个相关的对象:
sp
sp
sp
sp
在InputManager::start 函数中启动了两个进程:InputDispatcher、InputReader
InputReader类是Android系统中重要的部分, 在InputReader创建的时候,这里把InputDispatcher作为参数传递进来,然后以InputDispatcher作为参数构造出了QueuedInputListener对象。所以现在有这么一个关系:InputReader持有一个QueuedInputListener,而QueuedInputListener持有InputDispatcher对象。主要功能就是:void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector
...
//如果系统刚刚启动,或者有新的设备加入的话,timeoutMillis一般为0,意味着无需等待,可以立即返回;timeoutMillis一般为-1,意味着无限等待
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
...
}
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
//把QueuedInputListener中的消息全部都开始处理
mQueuedListener->flush();
}
继续处理该元事件:
[email protected]
-> [email protected] //该函数根据不同的事件类型调用不同的处理函数,如按键事件调用:
-> KeyboardInputMapper::process //如果是按键事件,则通用getEventHub()->mapKey 获取scanCode到keycode的映射。
-> KeyboardInputMapper::processKey
在该函数最终生成按键事件并通知道到输入监听器:
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);
在mEventHub->[email protected] 函数中读读取驱动中的RawEvent元数据的时候,会初始化对应Device的信息,如按键设备,会初始化按键的键值映射关系等。
Device初始化过程如下:
在接收到事件后有两个函数(scanDevicesLocked、readNotifyLocked)会触发初始化设备函数
openDeviceLocked传入相应的设备路径。通过该路径从内核中获取该设备的一些属性如:
device name.
identifier.bus
identifier.product
identifier.vendor
identifier.version
identifier.location
identifier.uniqueId
identifier.descriptor
->loadConfigurationLocked(device); 先load 该设备的配置文件, 在该函数中根据product、vendor、 version查找属性文件的路径:先system/usr/idc 如不存在,则从 /data/system/devices/idc.中查找。 见函数:--->getInputDeviceConfigurationFilePathByDeviceIdentifier
->loadKeyMapLocked 在该函数中直接调用[email protected]函数。 load函数中:如果配置文件中有指定本设备的*.kl 或*.kcm文件则使用该文件。如果传入的deviceIdenfifier文件名存在则用该该件。否则使用Generic.kl。所以MT6572平台最终使作的是Generic.kl 作为键盘映射文件。
-->[email protected] 如下:
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
在该函数中将*.kl 或*.kcm 同时load到系统。
键盘的映射在KeyLayoutMap::[email protected]中处理
字符的映射在KeyCharacterMap::[email protected]中处理
InputReader把事件发送到InputDispatcher
前面我们已经知道了,InputReader把元事件处理完毕后,构造了一个NotifyArgs,并把这个对象压入了QueuedInputListener的队列中,然后就返回了。
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,void NotifyKeyArgs::notify(const sp
listener->notifyKey(this);//调用InputDispatcher的对应的方法。
}
到这里,我们对于InputReader的功能的分析就完成了。总结一下,基本过程说就是:InputReader从EventHub中读取出来元事件,预处理加工这些元事件成为NotifyArgs,然后通过QueuedInputListener把他们通知给InputDispatcher
二、事件的分发流程(内容太多了,以下从其它地方拷贝)
在开始介绍InputDispatcher的功能之前,先看看Android文档对于其功能的描述:把输入事件发送到他的目标中去。他的目标可能是应用程序,也可能是WindowManagerService。如果是应用程序的话,可以通过registerInputChannel来定义输入事件的目标。我们已经了解InputDispatcher的唯一一个功能就是分发事件。知道了其功能之后,我们就开始分析InputDispatcher是如何实现这些功能的吧。先看他的构造函数,InputDispatcher创建了一个Looper,代码如下:
1 mLooper = new Looper(false);
这意味着,InputDispatcher有自己的Looper,没有和别人共用,信息也是自己在循环的。这个Looper是native层的Looper,由C++代码实现。在构建Looper过程中,新建了一个管道,这个管道仅仅起到了唤醒Looper,让其能从阻塞等待中返回。Looper中创建的管道是实现Looper功能的重要的方式,是通用的,不是仅仅为了InputDispatcher准备的。看完构造函数之后,我们接着分析InputDispatcher的功能,接着上节中的QueuedInputListener通知InputDispatcher有新的按键事件说起。这里还是接着上面的以按键的处理,接着看InputDispatcher是如何实现分发的,代码如下:
1 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
2 ...
3 KeyEvent event;//在这里通过传递进来的NotifyArgs为参数,构建KeyEvent
4 event.initialize(args->deviceId, args->source, args->action,
5 flags, args->keyCode, args->scanCode, metaState, 0,
6 args->downTime, args->eventTime);
7 //通过NativeInputManager把这个KeyEvent最终传递给WindowManagerService去处理
8 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
9 ...
10 bool needWake;
11 ...
12 int32_t repeatCount = 0;//这下面构建KeyEntry
13 KeyEntry* newEntry = new KeyEntry(args->eventTime,
14 args->deviceId, args->source, policyFlags,
15 args->action, flags, args->keyCode, args->scanCode,
16 metaState, repeatCount, args->downTime);
17
18 needWake = enqueueInboundEventLocked(newEntry);
19 mLock.unlock();
20
21 if (needWake) {//唤醒等待Looper
22 mLooper->wake();
23 }
24 }
我们先从代码中的line 8开始,这行代码的意思是,把KeyEvent发送出去,至于目的地是哪儿,在InputDispatcherPolicy中会有决定。是NativeInputManager实现了这个Policy,所以代码的执行会进入NativeInputManager中。
事件在入队前(before enqueue)的处理
在文章的前面已经说到过,NativeInputManager负责和系统的其他模块交互--是其功能之一。把这个KeyEvent传递到NativeInputManager之后,继续分发,最终会把这个KeyEvent传递到PhoneWindowManager中去处理这个事件,传递过程如下:NativeInputManager->interceptKeyBeforeQueueing ----> InputManagerService.interceptKeyBeforeQueueing ----> InputMonitor.interceptKeyBeforeQueueing ----> PhoneWindowManager.interceptKeyBeforeQueueing.大致过程是这样的,具体细节不再赘述。在传递过程中是跨线程的。通过这一系列的方法的名字可以看出,是在事件进入InputDispatcher的队列之前,进行的一些处理。在PhoneWindowManager处理事件之后,会有一个返回值来标记这一事件处理的结果是怎样的,为后面的事件进入队列做准备。在PhoneWindowManager对事件进行前期的拦截处理过程时,一般首先把事件都标记上PASS_TO_USER,即这个事件要交给应用程序去处理,但是在处理过程中决定,有些事件是没必要传递给应用程序的,比如:在通过过程中按下音量相关的事件,挂断电话的事件,power键的处理,以及拨打电话的事件。这些事件的处理结果都是不必传递到应用程序的,这个结果最为返回值,最终会一步一步地返回到NativeInputManager中,这个返回值会作为NativeInputManager的policyFlags的一部分,供InputDispatcher使用。在PhoneWindowManager对事件处理完成后,才会把这个事件构造成为一个形式为EventEntry放入队列。到这里,我们的工作仍在InputReaderThread的线程中,虽然是对InputDispatcher的操作。接下来才是真正进入InputDispatcherTread线程对InputDispatcher操作。通过InputDispatcher的mLooper的wake方法,唤醒InputDispatcherThread线程。关于Looper如何在wake时是如何通过管道的方式去实现的,这个过程应该放在一篇单独的文章中详细地去说明,在以后的文章中,我会说到Looper在native实现时的一些特点的。这里,我们知道InputDispatcherThread线程被唤醒了。如果你已忘记InputDispatcherThread线程是何时被阻塞,那就回头再重新看看吧。学习别人的思路就是这样,反复回头看,才能不至于迷失在别人的思维中。然后就开始执行InputDispatcher的threadLoop函数,之后就调用InputDispatcher的dispatchOnce方法,代码如下:
1 void InputDispatcher::dispatchOnce() {
2 nsecs_t nextWakeupTime = LONG_LONG_MAX;//应该是64位二进制所能表示的最大值,大概是2^63-1,即9223372036854775807
3 {
4 AutoMutex _l(mLock);
5 mDispatcherIsAliveCondition.broadcast();
6 //如果没有等待执行的命令的话,就开始一次循环分发。在循环过程中,可能会有一些命令产生。这里的命令大概是模式设计中的:命令模式吧
7 if (!haveCommandsLocked()) {
8 dispatchOnceInnerLocked(&nextWakeupTime);
9 }
10 //如果任何等待执行的命令的话,那么就执行这些命令;假如有命令已经执行了,那么下次poll的时候直接唤醒
11 if (runCommandsLockedInterruptible()) {
12 nextWakeupTime = LONG_LONG_MIN;//#define LONG_LONG_MIN (-__LONG_LONG_MAX__-1LL), 即-9223372036854775808
13 }
14 } // release lock
15
16 nsecs_t currentTime = now();
17 int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
18 mLooper->pollOnce(timeoutMillis);
19 }
InputDispatcher的主要功能就在这段代码中,这是个轮廓。要想知道具体的功能的实现,还要需要逐步分析下去。先看line7和line8中的代码,如果是一次正常的分发循环(dispatch loop)的话,应该是没有等待执行的命令。为什么会没有等待执行的命令,在后面会说到原因,先不要着急。所以接下就开始dispatchOnceInnerLocke方法,从这个方法的名字可以看出,这应该是功能的核心实现部分。看其代码是如何实现的:
1 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
2 nsecs_t currentTime = now();
3 //如果等待处理的事件不存在的话
4 if (! mPendingEvent) {
5 if (mInboundQueue.isEmpty()) {
6 ...//省略了,当等待处理事件不存在且事件队列为空的时候的处理
7 } else {//从事件队列的头部取出一个事件
8 mPendingEvent = mInboundQueue.dequeueAtHead();
9 traceInboundQueueLengthLocked();
10 }
11 if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
12 //通知某些Activity一些事件的发生,通过这个方法的名字可以联想一下,一些社交网站中的“捅一下”应用,或者QQ中的震动窗口功能,
13 pokeUserActivityLocked(mPendingEvent);//这个方法的功能就类似于那些作用。只不过这里主要是用来“捅一下”PowerManagerService的
14 }
15
16 // Get ready to dispatch the event.
17 resetANRTimeoutsLocked();
18 }
19 //现在我们有事件需要开始处理了
20 ALOG_ASSERT(mPendingEvent != NULL);
21 bool done = false;
22 DropReason dropReason = DROP_REASON_NOT_DROPPED;//在开始处理之前,所有的事件都不必丢弃
23 if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
24 dropReason = DROP_REASON_POLICY;
25 } else if (!mDispatchEnabled) {
26 dropReason = DROP_REASON_DISABLED;
27 }
28
29 if (mNextUnblockedEvent == mPendingEvent) {
30 mNextUnblockedEvent = NULL;
31 }
32
33 switch (mPendingEvent->type) {
34 ...//省略了对于config change类别的事件的处理
35 ...//省略了对于设备重置事件的处理
36 case EventEntry::TYPE_KEY: {
37 KeyEntry* typedEntry = static_cast(mPendingEvent);
38 if (isAppSwitchDue) {//下面这些内容,是对于事件是否需要丢弃的分析
39 if (isAppSwitchKeyEventLocked(typedEntry)) {
40 resetPendingAppSwitchLocked(true);
41 isAppSwitchDue = false;
42 } else if (dropReason == DROP_REASON_NOT_DROPPED) {
43 dropReason = DROP_REASON_APP_SWITCH;
44 }
45 }
46 if (dropReason == DROP_REASON_NOT_DROPPED
47 && isStaleEventLocked(currentTime, typedEntry)) {
48 dropReason = DROP_REASON_STALE;
49 }
50 if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
51 dropReason = DROP_REASON_BLOCKED;
52 }//无论事件是否要被丢弃,都要经过如下的处理
53 done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
54 break;
55 }
56 ...//省略了对于motion事件的处理
57 }
58 ...
59 }
这个方法中的大部分功能都已经在代码中注释了,主要就是取出事件,分析是否需要丢弃,然后就是开始按照类型分发事件,我们假设的是按键事件,所以接下来就是调用dispatchKeyLocked方法来分发。
1 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
2 DropReason* dropReason, nsecs_t* nextWakeupTime) {
3 if (! entry->dispatchInProgress) {
4 ...//省略了对于重复事件在各种情况下的处理
5 }
6
7 ...//在入队列之前,对于事件有个一次intercept,这里是对事件的intercept结果的处理
8 Vector inputTargets;
9 int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
10 entry, inputTargets, nextWakeupTime);//寻找事件发送到的目标窗口
11 // 分发事件
12 dispatchEventLocked(currentTime, entry, inputTargets);
13 return true;
14 }
这个方法中主要就是寻找到事件应该分发到的目标,可能是应用窗口.这个目标应用的窗口寻找与应用程序启动时设置到窗口有关。在下一小节中会说到这个窗口是如何找到的。其代码不是很复杂,自己看看的话也很容易能够明白。其他的内容在上面的注释中也有说明。下面还是将注意力集中在事件分发上,注意这里传入dispatchEventLocked的参数中inputTargets是复数,也就是说可能有多个目标。所以在方法dispatchEventLocked中就是根据每一个target对应的inputChannel找到connection,然后 prepareDispatchCycleLocked使用这个connection把事件逐个分发到target中。 在prepareDispatchCycleLocked方法中,主要就是根据事件是否可以分割,分别把事件放入队列。在入队列的之后,InputPublisher的发布事件的队列就不再为空,然后会调用 startDispatchCycleLocked方法,通过InputPublisher开始发布事件。大致过程如此,为了减少篇幅,这里就不再列出代码了。流程图如下:
整个的流程图太大了,不太方便,这里仅仅是其中的一部分。说明一点:图中Looper到InputDispatcher中的dispatcherOnce不是调用关系,只是Looper把其所在的进程即InputDispatcherThread线程给唤醒,所以开始执行dispatchOnce。这里到最后就是调用InputPublisher的publishKeyEvent方法,把事件发布出去。在前面我们说到过这么一个问题,等待执行的命令为什么在一次正常的事件分发之后应该为空?这些命令产生的地方分别在pokeUserActivity方法中, 和dispatchKeyLocked中等等在使用postCommand把命令放入队列的地方。在上面这个过程执行完毕后,会返回到dispatchOnce方法中,接着往下执行,也就是执行代码:
runCommandsLockedInterruptible()
也就是前面dispatchOnce方法的line 11. 这个方法的功能就是执行之前放入命令队列的命令。具体的代码不再列出。到这里,关于InputDispatcher的功能--唯一的一个功能--事件分发,就算介绍完了。