该系列文章总纲链接:专题分纲目录 Android Framework 输入子系统
本章关键点总结 & 说明:
以上是迭代导图,主要关注➕ 基础部分->调试工具部分即可,同时上图是总图,局部显示的有点小,局部截图,如下所示:
本章节的思维导图放大后如上所示,这里主要研究Input命令的使用和原理。
input命令简介:该命令在调试中是很常见的命令,尤其是针对无法触摸的屏幕,比如VR,这时使用input命令调试将会是常态,因此这里简单的分析下工具的常见用法以及源码实现和其中的原理
1 工具使用:
input命令支持的输入源如下所示:
常见的使用说明案例如下:
input text 1234 实际向界面注入1234文字,有输入框,能明显看到效果
input keyevent 4 键盘事件,4 为返回
input tap 100 300 单击触屏事件 ,模拟点击x=100 y = 300 位置
input swipe 100 300 500 300 触屏滑动事件,模拟滑动,从x=100 y=300 滑动到x=500 y=300位置
input swipe 500 500 500 500 1000,同上,只不过是在1000ms时间内完成而已
input swipe 100 100 100 100 1000 //在 100 100 位置长按 1000毫秒,即长按
input press 模拟按下轨迹球
input roll 100 300 模拟轨迹球滑动 x 方向100 y方向300
2 源码概要要解读:
源码是java实现的,路径为:frameworks\base\cmds\input\src\com\android\commands\input\input.java,这里从main函数开始分析,代码如下所示:
public static void main(String[] args) {
(new Input()).run(args);
}
继续分析run的实现,代码如下所示:
private void run(String[] args) {
if (args.length < 1) {
showUsage();
return;
}
int index = 0;
String command = args[index];
int inputSource = InputDevice.SOURCE_UNKNOWN;
if (SOURCES.containsKey(command)) {
inputSource = SOURCES.get(command);
index++;
command = args[index];
}
final int length = args.length - index;
try {
if (command.equals("text")) {
if (length == 2) {
inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
sendText(inputSource, args[index+1]);
return;
}
} else if (command.equals("keyevent")) {
if (length >= 2) {
final boolean longpress = "--longpress".equals(args[index + 1]);
final int start = longpress ? index + 2 : index + 1;
inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
if (length > start) {
for (int i = start; i < length; i++) {
int keyCode = KeyEvent.keyCodeFromString(args[i]);
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
}
sendKeyEvent(inputSource, keyCode, longpress);
}
return;
}
}
} else if (command.equals("tap")) {
if (length == 3) {
inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
sendTap(inputSource, Float.parseFloat(args[index+1]),
Float.parseFloat(args[index+2]));
return;
}
} else if (command.equals("swipe")) {
int duration = -1;
inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
switch (length) {
case 6:
duration = Integer.parseInt(args[index+5]);
case 5:
sendSwipe(inputSource,
Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
duration);
return;
}
} else if (command.equals("press")) {
inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
if (length == 1) {
sendTap(inputSource, 0.0f, 0.0f);
return;
}
} else if (command.equals("roll")) {
inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
if (length == 3) {
sendMove(inputSource, Float.parseFloat(args[index+1]),
Float.parseFloat(args[index+2]));
return;
}
} else {
System.err.println("Error: Unknown command: " + command);
showUsage();
return;
}
} catch (NumberFormatException ex) {
}
System.err.println(INVALID_ARGUMENTS + command);
showUsage();
}
run起来后,我们关注 各种命令的处理,不如text,接下来我们沿着一条线来追踪命令的执行,抽取上面代码如下:
if (command.equals("text")) {
if (length == 2) {
inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
sendText(inputSource, args[index+1]);
return;
}
} else if...
这里继续分析sendText,代码实现如下:
private void sendText(int source, String text) {
//...
for(int i = 0; i < events.length; i++) {
KeyEvent e = events[i];
if (source != e.getSource()) {
e.setSource(source);
}
injectKeyEvent(e);
}
}
这里经过一些列处理,最后调用到了injectKeyEvent(e);,继续分析,代码如下:
private void injectKeyEvent(KeyEvent event) {
Log.i(TAG, "injectKeyEvent: " + event);
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
这里最终调用的是InputManager的injectInputEvent方法,实际上通过其他的命令分析,最终都会走到injectInputEvent方法,不同的仅仅是参数不同罢了,那么接下来就简单分析下injectInputEvent的实现,代码如下:
public boolean injectInputEvent(InputEvent event, int displayId, int mode) {
return injectInputEventInternal(event, displayId, mode);
}
继续深入分析,代码如下:
private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) {
//...
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
final int result;
try {
result = nativeInjectInputEvent(mPtr, event, displayId, pid, uid, mode,
INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
} finally {
Binder.restoreCallingIdentity(ident);
}
//...
}
继续分析nativeInjectInputEvent,代码实现如下:
static jint nativeInjectInputEvent(JNIEnv* env, jclass clazz,
jlong ptr, jobject inputEventObj, jint displayId, jint injectorPid, jint injectorUid,
jint syncMode, jint timeoutMillis, jint policyFlags) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
//...
return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
& keyEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,
uint32_t(policyFlags));
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
//...
return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
motionEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,
uint32_t(policyFlags));
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
}
}
这里将传入的inputEventObj转换成KeyEvent,并最后调用im->getInputManager()->getDispatcher(),即InputDispatcher的injectInputEvent,它的代码如下:
int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags) {
//...
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
//...
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
}
mLock.lock();
firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
keyEvent->getDeviceId(), keyEvent->getSource(),
policyFlags, action, flags,
keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
keyEvent->getRepeatCount(), keyEvent->getDownTime());
lastInjectedEntry = firstInjectedEntry;
break;
}
//...
}
InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
injectionState->injectionIsAsync = true;
}
injectionState->refCount += 1;
lastInjectedEntry->injectionState = injectionState;
bool needWake = false;
for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
EventEntry* nextEntry = entry->next;
needWake |= enqueueInboundEventLocked(entry);
entry = nextEntry;
}
mLock.unlock();
if (needWake) {
mLooper->wake();
}
//...injectionResult结果处理
return injectionResult;
}
这里实际上就是把keyEvent转换为entry并将其放入到mInboundQueue中,对于其他事件也是类似,采用这样的方式实现模拟底层事件的产生。