在Android系统中,除了真实的输入设备可以产生事件之外,我们也可以通过软件的方式,模拟一个输入事件,比如模拟一个点击事件,模拟一个按键事件等等。
怎么模拟一个输入事件
1,在adb命令行使用input命令模拟输入事件
源码路径:frameworks\base\cmds\input\src\com\android\commands\input\Input.java
@Override
public void onShowUsage(PrintStream out) {
out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
out.println();
out.println("The sources are: ");
for (String src : SOURCES.keySet()) {
out.println(" " + src);
}
out.println();
out.printf("-d: specify the display ID.\n"
+ " (Default: %d for key event, %d for motion event if not specified.)",
INVALID_DISPLAY, DEFAULT_DISPLAY);
out.println();
out.println("The commands and default sources are:");
out.println(" text <string> (Default: touchscreen)");
out.println(" keyevent [--longpress] <key code number or name> ..."
+ " (Default: keyboard)");
out.println(" tap <x> <y> (Default: touchscreen)");
out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ " (Default: touchscreen)");
out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ " (Default: touchscreen)");
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
}
从使用方法说明中可以看出,使用input keyevent 可以模拟一个按键事件,使用input tap 可以模拟一个点击事件。他们都是通过调用IMS的injectInputEvent方法向系统注入事件,只是传入的event类型不一致
InputManager.getInstance().injectInputEvent(event,InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
2,在代码中模拟输入事件
对于使用系统源码编译的APK,当然可以直接调用IMS的injectInputEvent来模拟一个输入事件。
如果APP不是放在系统源码中编译,直接使用这个方法的话,肯定是编译不过的,这时候我们可以借助Instrumentation类来实现模拟一个输入事件,例如注入一个MotionEvent 的down事件
Instrumentation instrumentation = new Instrumentation();
MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 100, 500, 0.0f, 1.0f, 0, 1.0f, 1.0f,getInputDeviceId(InputDevice.SOURCE_TOUCHSCREEN), 0);
instrumentation.sendPointerSync(event);
//
private static int getInputDeviceId(int inputSource) {
final int DEFAULT_DEVICE_ID = 0;
int[] devIds = InputDevice.getDeviceIds();
for (int devId : devIds) {
InputDevice inputDev = InputDevice.getDevice(devId);
if (inputDev.supportsSource(inputSource)) {
return devId;
}
}
return DEFAULT_DEVICE_ID;
}
3,对于自己应用内的事件注入,是有权限的,但是如果想注入事件到其它的应用,是需要申请INJECT_EVENTS权限的,而这个权限是需要系统签名的应用才能申请
<permission android:name="android.permission.INJECT_EVENTS"
android:protectionLevel="signature" />
所以,应用需要有系统签名,并且申请了INJECT_EVENTS权限,才能注入事件到其它的应用
注入事件的原理分析
不管上面使用哪种方式实现的事件注入,最终都是调用IMS的injectInputEvent方法,injectInputEvent方法中直接调用injectInputEventInternal,从这个方法开始分析
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private boolean injectInputEventInternal(InputEvent event, 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();//记录调用者的pid
final int uid = Binder.getCallingUid();//记录调用者的uid
final long ident = Binder.clearCallingIdentity();
final int result;
try {
result = nativeInjectInputEvent(mPtr, event, pid, uid, mode,
INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
} finally {
Binder.restoreCallingIdentity(ident);
}
//省略
}
调用Native方法,需要传入调用者的pid和uid,因为后续需要判断调用者是否有注入事件的权限。
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputEventObj, 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;
}
const int32_t result =
im->getInputManager()->getDispatcher()->injectInputEvent(&keyEvent, injectorPid,
injectorUid, syncMode,
std::chrono::milliseconds(
timeoutMillis),
uint32_t(policyFlags));
return static_cast<jint>(result);
} 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;
}
const int32_t result =
(jint)im->getInputManager()
->getDispatcher()
->injectInputEvent(motionEvent, injectorPid, injectorUid, syncMode,
std::chrono::milliseconds(timeoutMillis),
uint32_t(policyFlags));
return static_cast<jint>(result);
} else {
jniThrowRunti
对于按键事件以及触摸事件,都是调用InputDispatcher的injectInputEvent方法
int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
int32_t injectorUid, int32_t syncMode,
std::chrono::milliseconds timeout, uint32_t policyFlags) {
//省略
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
//省略
KeyEvent keyEvent;
keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
incomingKey.getDownTime(), incomingKey.getEventTime());
mLock.lock();
KeyEntry* injectedEntry =
new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(),
VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
incomingKey.getDisplayId(), policyFlags, action, flags, keyCode,
incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
incomingKey.getDownTime());//构造KeyEntry
injectedEntries.push(injectedEntry);//放入injectedEntries
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
//省略
MotionEntry* injectedEntry =
new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID,
motionEvent->getSource(), motionEvent->getDisplayId(),
policyFlags, action, actionButton, motionEvent->getFlags(),
motionEvent->getMetaState(), motionEvent->getButtonState(),
motionEvent->getClassification(), motionEvent->getEdgeFlags(),
motionEvent->getXPrecision(), motionEvent->getYPrecision(),
motionEvent->getRawXCursorPosition(),
motionEvent->getRawYCursorPosition(),
motionEvent->getDownTime(), uint32_t(pointerCount),
pointerProperties, samplePointerCoords,
motionEvent->getXOffset(), motionEvent->getYOffset());
injectedEntries.push(injectedEntry);
//省略
}
bool needWake = false;
while (!injectedEntries.empty()) {
needWake |= enqueueInboundEventLocked(injectedEntries.front());//放入队列
injectedEntries.pop();
}
mLock.unlock();
if (needWake) {
mLooper->wake();//唤醒InputDispatcher线程
}
}
可以看出,注入事件也是放入iq队列。
总结
使用软件模拟注入事件也是放入iq队列,只是不需要经过InputReader的处理。