我们可以在手机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;
- }
- 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命令模拟按键的过程就比较清楚了。