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命令模拟按键的过程就比较清楚了。
- android input进程(模拟按键)
- android input命令 模拟按键
- android input命令 模拟按键
- input 模拟按键
- [Android][adb]input笔记--模拟-按键-点击-滑动事件
- Android adb input命令模拟滑动 按键 点击事件
- android 软按键 模拟按键
- android 模拟按键
- android 应用模拟按键
- android 模拟按键
- Android模拟按键
- Android 模拟物理按键
- Android模拟按键
- 命令模拟android 按键
- android 按键模拟
- Android 模拟物理按键
- Android 模拟按键汇总
- Android 实现模拟按键
- win7作为服务器,vm12虚拟机centos6访问win7上的ftp文件夹
- 三色旗问题
- activiti退回到任意节点实例,不支持并行网关
- Linux Shell系列教程之(四)Shell注释
- 64位Ubuntu系统如何运行32位软件
- android input进程(模拟按键)
- 一个神人创造了验证码,又让验证码做出了巨大贡献
- 剑指--重建二叉树
- java导出Excel文件,直接可以下载,然后更新状态,刷新页面
- 关于imageloader的一些理解
- 94. Binary Tree Inorder Traversal #Medium
- 多个if else替换为一个if判断
- java中的线程池优点以及处理原理
- js prototype介绍