该系列文章总纲链接:专题分纲目录 Android Framework 输入子系统


本章关键点总结 & 说明:

Android Framework 输入子系统 (10)Input命令解读_android

以上是迭代导图,主要关注➕ 基础部分->调试工具部分即可,同时上图是总图,局部显示的有点小,局部截图,如下所示:

Android Framework 输入子系统 (10)Input命令解读_输入子系统_02

本章节的思维导图放大后如上所示,这里主要研究Input命令的使用和原理。

input命令简介:该命令在调试中是很常见的命令,尤其是针对无法触摸的屏幕,比如VR,这时使用input命令调试将会是常态,因此这里简单的分析下工具的常见用法以及源码实现和其中的原理

1 工具使用:

input命令支持的输入源如下所示:

Android Framework 输入子系统 (10)Input命令解读_android_03

常见的使用说明案例如下:

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中,对于其他事件也是类似,采用这样的方式实现模拟底层事件的产生。