http://wenku.baidu.com/view/1f6650906bec0975f465e22f.html
Input Subsystem分析
Android2.3.7的Input子系统由驱动、Native层InputManager、InputManager JNI、Java层InputManager组成。
Input子系统的驱动被封装为字符设备,目录位于/dev/input,上层架构通过扫描该目录,得到输入设备,open()设备,read()来自底层驱动的输入事件,再由上层处理转发。
Native层InputManager有:
EventHub.cpp 使用poll机制,从字符设备获取事件。
InputReader.cpp 负责从EventHub获取事件并交给InputDispatcher进行分发。它有三个附属类协助其实现该层的功能,InputDevice、InputMapper(其子类有:SwitchInputMapper、KeyboardInputMapper、TouchInputMapper、MouseInputMapper、SingleTouchInputMapper、MultiTouchInputMapper等各种输入类型的映射)将事件分类交给InputDispatcher。还有个InputReaderThread负责创建InputReader线程,由InputManager类管理,该线程会受到Poll机制的阻塞。
InputDispatcher.cpp负责将事件分发给Connection(即 foreground target,猜测是当前活动UI的代理)。他内部采用queue存储事件,并使用Looper类(使用epoll实现)实现对事件接收者的管理,一旦有事件来到,首先检查queue是否为空并插入事件,不空时立即唤醒Looper,进入事件处理循环。这里同样采用线程循环,名字是InputDispatcherThread,也是由InputManager类初始化,该线程同样会受到Poll机制的阻塞。InputDispatcher类比较复杂,其内部流程还不是很清楚,还有一些附属类尚未研究。
InputManager.cpp负责管理InputReaderThread和InputDispatcherThread线程,如它们的初始化、start和stop。
以上几个类都是从其相应的Interface抽象类继承下来的,表明它们是被作为独立的组件设计的,我们可以替换它们。但是JNI层依赖具体的InputManager,2.3.7版本的InputManager实现比较简单,依附于WindowManager服务,4.0代码将InputManager也提升为一个service,可能输入响应要提高一个级别。
JNI层InputManager有:
com_android_server_InputManager.cpp负责初始化InputManager,向上面Java层提供Native调用。它继承自InputReaderPolicyInterface和InputDispatcherPolicyInterface,并将自己作为参数传递给InputReader和InputDispatcher,大概是用来实现一些策略。
它提供的功能很多,如registerInputChannel()函数提供给应用程序注册Input系统以实现事件输入,该机制被包装为InputChannel类,在Native层又被封装进Connection类,被Looper添加为事件接受者。
Java层InputManager有:
InputManager.java几乎是对JNI层的封装,并为JNI提供了回调。它的初始化是在WindowManagerService的构造函数中,并在这里做一些Input子系统的初始化的工作,如设置display的大小。它是作为WindowManagerService的一个功能类来使用,基本上是又将InputManager.java封装了一次。其他附属类没有分析。
问题:InputManager的Native层C++代码没有入口函数,都是以线程的方式被上层Java框架经由JNI启动。问题是该线程所属的进程是谁?
自动化测试工具Monkey向系统插入Input事件的方法
Android2.3.7中的Monkey实现了三种途径向系统插入Input事件:
1. 随机生成Input事件,对应类为MonkeySourceRandom
2. 从脚本读取Input事件,对应类为MonkeySourceScript
3. 从socket读取Input事件,对应类为MonkeySourceNetwork
这三个类均继承自MonkeyEventSource类,它们将从不同源获取到的event放进内部queue,在Monkey的runMonkeyCycles()循环中取出这些event插入系统。
代码:
MonkeyEvent ev = mEventSource.getNextEvent();
if (ev != null) {
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
这里还是实现了几种 MonkeyEvent模拟系统里面的Input Event,如MonkeyKeyEvent、MonkeyMotionEvent、MonkeyPowerEvent、MonkeyNoopEvent、MonkeyFlipEvent、MonkeyCommandEvent、MonkeyActivityEvent等。它们各自实现了 injectEvent()函数,因为是模拟事件,所以不是所有子类的都会向系统底层插入event。
例如 MonkeyCommandEvent是执行一个命令,代码如下:
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
if (mCmd != null) {
//Execute the shell command
try {
java.lang.Process p = Runtime.getRuntime().exec(mCmd);
int status = p.waitFor();
System.err.println("// Shell command " + mCmd + " status was " + status);
} catch (Exception e) {
System.err.println("// Exception from " + mCmd + ":");
System.err.println(e.toString());
}
}
return MonkeyEvent.INJECT_SUCCESS;
}
向系统底层插入event的代码会这样写:iwm.injectKeyEvent(getEvent(), false) 插入的是Key事件。
在android的Java层、JNI层、Native C++层都实现了类似的函数,它们是层层向下调用的关系,最终Input event会被插入到InputDispatcher的内部queue里面,进入事件分发循环。
从上至下依次实现:
1. WindowManagerService.java中,injectKeyEvent()等;
2. InputManager.java中,injectInputEvent();
3. com_android_server_InputManager.cpp中,android_server_InputManager_nativeInjectInputEvent();
4. InputDispatcher.cpp中,injectInputEvent()。
自动化测试工具Monkeyrunner的工作方式
Monkeyrunner是基于ddms和Monkey开发的自动化测试工具,它使用Python作为脚本对自动化测试过程进行控制。有三个类实现其功能:
1. MonkeyRunner 在PC端调用ddms库的功能,完成设备socket连接、adb初始化、向Monkey发送event和command、截屏等。在手机端由Monkey完成event的插入并执行一些command。
2. MonkeyRecorder 用XML格式记录执行过的command和event,还提供了对记录脚本的打包功能,该脚本不是Python脚本,可用于Monkey的回放。
3. ScriptRunner 负责加载Python脚本并执行它。