android input进程(模拟按键)

来源:互联网 发布:mac 鼠标 触摸板 编辑:程序博客网 时间:2024/05/22 14:44

我们可以在手机adb shell中,使用input来模拟按键,和之前的sm类似,input也是一个进程,在framework/base/cmds目录下。

一、Input源码

下面我们先看下input的源码:

 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;                    }                }            }.............

我们再来看看sendKeyEvent函数

    private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {        long now = SystemClock.uptimeMillis();        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));        if (longpress) {            injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,                    inputSource));        }        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));    }

最后再来看看injectKeyEvent函数,其主要还是调用了,InputManager中的injectInputEvent函数。

    private void injectKeyEvent(KeyEvent event) {        Log.i(TAG, "injectKeyEvent: " + event);        InputManager.getInstance().injectInputEvent(event,                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);    }


二、InputManager相关代码

我们再来看看InputManager的injectInputEvent函数,最后还是调用了InputManagerService的injectInputEvent函数。

    public boolean injectInputEvent(InputEvent event, int mode) {        if (event == null) {            throw new IllegalArgumentException("event must not be null");        }        if (mode != INJECT_INPUT_EVENT_MODE_ASYNC                && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH                && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {            throw new IllegalArgumentException("mode is invalid");        }        try {            return mIm.injectInputEvent(event, mode);//调用了InputManagerService的injectInputEvent函数        } catch (RemoteException ex) {            return false;        }    }

我们再来看看InputManagerService的injectInputEvent函数

    @Override // Binder call    public boolean injectInputEvent(InputEvent event, int mode) {        return injectInputEventInternal(event, Display.DEFAULT_DISPLAY, mode);    }

我们再来看injectInputEventInternal函数

    private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) {        if (event == null) {            throw new IllegalArgumentException("event must not be null");        }        if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {            throw new IllegalArgumentException("mode is invalid");        }        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,//主要看这个jni函数                    INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);        } finally {            Binder.restoreCallingIdentity(ident);        }        switch (result) {            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:                Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");                throw new SecurityException(                        "Injecting to another application requires INJECT_EVENTS permission");            case INPUT_EVENT_INJECTION_SUCCEEDED:                return true;            case INPUT_EVENT_INJECTION_TIMED_OUT:                Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");                return false;            case INPUT_EVENT_INJECTION_FAILED:            default:                Slog.w(TAG, "Input event injection from pid " + pid + " failed.");                return false;        }    }


三、native层代码

上面这个函数主要调用了nativeInjectInputEvent这个native函数。

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)) {        KeyEvent keyEvent;        status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);        if (status) {            jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");            return INPUT_EVENT_INJECTION_FAILED;        }        return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(                & keyEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,                uint32_t(policyFlags));    } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {        const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);        if (!motionEvent) {            jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");            return INPUT_EVENT_INJECTION_FAILED;        }        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;    }}

这个函数先根据event的种类,有KeyEvent,MoveEvent来调用相关函数。我们再来看看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) {    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);    policyFlags |= POLICY_FLAG_INJECTED;    if (hasInjectionPermission(injectorPid, injectorUid)) {        policyFlags |= POLICY_FLAG_TRUSTED;    }    EventEntry* firstInjectedEntry;    EventEntry* lastInjectedEntry;    switch (event->getType()) {    case AINPUT_EVENT_TYPE_KEY: {        const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);        int32_t action = keyEvent->getAction();        if (! validateKeyEvent(action)) {            return INPUT_EVENT_INJECTION_FAILED;        }        int32_t flags = keyEvent->getFlags();        if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {            policyFlags |= POLICY_FLAG_VIRTUAL;        }        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;    }......

这个函数和notifyKey函数很像,notifyKey函数是正常走按键流程在dispatchReader中调用的函数。这里也会想notifyKey一样,先调用PhoneWindowManager的interceptKeyBeforeQueueing函数,然后根据不同类型的Event,然后创建EventEntry,最后调用了enqueueInboundEventLocked函数,这个函数之前在按键流程中分析过了。最后也会调用mLooper->wake函数,把InputDispatcherThread线程唤醒,然后执行dispatchOnce函数:

bool InputDispatcherThread::threadLoop() {    mDispatcher->dispatchOnce();    return true;}
dispatchOnce函数会调用dispatchOnceInnerLocked函数,最终发送按键消息到应用。当执行到mLooper->pollOnce函数时,会阻塞。这个在之前的消息机制中介绍过。
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);}

这样整个adb input命令模拟按键的过程就比较清楚了。




1 0