您的位置:首页 > 移动开发 > Android开发

android input命令 模拟按键

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

一、Input源码

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

[cpp]
view plain
copy





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函数

[cpp]
view plain
copy





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函数。

[cpp]
view plain
copy





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函数。

[cpp]
view plain
copy





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函数

[cpp]
view plain
copy





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

我们再来看injectInputEventInternal函数

[cpp]
view plain
copy





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函数。

[cpp]
view plain
copy





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函数

[cpp]
view plain
copy





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函数:

[cpp]
view plain
copy





bool InputDispatcherThread::threadLoop() {  
    mDispatcher->dispatchOnce();  
    return true;  
}  

dispatchOnce函数会调用dispatchOnceInnerLocked函数,最终发送按键消息到应用。当执行到mLooper->pollOnce函数时,会阻塞。这个在之前的消息机制中介绍过。

[cpp]
view plain
copy





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命令模拟按键的过程就比较清楚了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: