文章目录
1. Input命令滑动事件
1.1 input命令使用
input --help查询使用
130|msm8996_gvmq:/ # input --help
Error: Unknown command: --help
Usage: input [<source>] <command> [<arg>...]
The sources are:
dpad
keyboard
mouse
touchpad
gamepad
touchnavigation
joystick
touchscreen
stylus
trackball
The commands and default sources are:
text <string> (Default: touchscreen)
keyevent [--longpress] <key code number or name> ... (Default: keyboard)
tap <x> <y> (Default: touchscreen)
swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
press (Default: trackball)
roll <dx> <dy> (Default: trackball)
1. keyevent指的是android对应的keycode,比如home键的keycode=3,back键的keycode=4.
@frameworks/base/core/java/android/view/KeyEvent.java
adb shell input keyevent 3
2. tap模拟的是touch屏幕的事件,只需给出x、y坐标即可。
此x、y坐标对应的是真实的屏幕分辨率,所以要根据具体手机具体看,比如你想点击屏幕(x, y) = (250, 250)位置:
adb shell input tap 250 250
3. 关于swipe同tap是一样的,只是他是模拟滑动的事件,给出起点和终点的坐标即可。例如从屏幕(250, 250), 到屏幕(300, 300)即
adb shell input swipe 250 250 300 300
1.2 input命令源码解析
input命令最终是调用 InputManager.getInstance().injectInputEvent(event来发送事件
@frameworks/base/cmds/input/src/com/android/commands/input/Input.java
private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
if (duration < 0) {
duration = 300;
}
long now = SystemClock.uptimeMillis();
injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
long startTime = now;
long endTime = startTime + duration;
while (now < endTime) {
long elapsedTime = now - startTime;
float alpha = (float) elapsedTime / duration;
injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
lerp(y1, y2, alpha), 1.0f);
now = SystemClock.uptimeMillis();
}
injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
}
/**
* Builds a MotionEvent and injects it into the event stream.
*
* @param inputSource the InputDevice.SOURCE_* sending the input event
* @param action the MotionEvent.ACTION_* for the event
* @param when the value of SystemClock.uptimeMillis() at which the event happened
* @param x x coordinate of event
* @param y y coordinate of event
* @param pressure pressure of event
*/
private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
final float DEFAULT_SIZE = 1.0f;
final int DEFAULT_META_STATE = 0;
final float DEFAULT_PRECISION_X = 1.0f;
final float DEFAULT_PRECISION_Y = 1.0f;
final int DEFAULT_EDGE_FLAGS = 0;
MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
event.setSource(inputSource);
Log.i(TAG, "injectMotionEvent: " + event);
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
IInputManager没有displayID接口的调用,因此无法发送事件到其他屏
@frameworks/base/core/java/android/hardware/input/IInputManager.aidl
boolean injectInputEvent(in InputEvent ev, int mode);
@Override // Binder call
public boolean injectInputEvent(InputEvent event, int mode) {
return injectInputEventInternal(event, Display.DEFAULT_DISPLAY, mode);
}
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,
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;
}
}
JNI最终还是调用InputDispatcher来完成事件发送
@frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
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;
}
}
2. SendEvent滑动事件
通过getevent 观察滑动事件,发现滑动事件就是一个down事件,加上很多move事件,在最后加一个up事件,因此是否通过sendevent发送这样的事件就可以了
//getevent -l
/dev/input/event0: EV_ABS ABS_MT_TRACKING_ID 0000001a
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 0000057b
/dev/input/event0: EV_ABS ABS_MT_POSITION_Y 000000b7
/dev/input/event0: EV_KEY BTN_TOUCH DOWN
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 0000056f
/dev/input/event0: EV_ABS ABS_MT_POSITION_Y 000000b8
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 0000027b
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 00000273
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 00000272
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_POSITION_X 00000271
/dev/input/event0: EV_SYN SYN_REPORT 00000000
/dev/input/event0: EV_ABS ABS_MT_TRACKING_ID ffffffff
/dev/input/event0: EV_KEY BTN_TOUCH UP
/dev/input/event0: EV_SYN SYN_REPORT 00000000
### getevent
/dev/input/event0 0003 0039 00000021
/dev/input/event0 0003 0035 00000770 //ABS_MT_POSITION_X 0035
/dev/input/event0 0003 0036 00000087 //ABS_MT_POSITION_Y 0036
/dev/input/event0 0001 014a 00000001 //down
/dev/input/event0 0000 0000 00000000
/dev/input/event0 0000 0000 00000000
/dev/input/event0 0003 0035 0000054c
/dev/input/event0 0003 0036 00000072
/dev/input/event0 0000 0000 00000000
/dev/input/event0 0003 0039 ffffffff
/dev/input/event0 0001 014a 00000000 //up
/dev/input/event0 0000 0000 00000000 //SYN_REPORT
3. shell脚本连续发送滑动事件
shell脚本可用于linux客户端测试
和5最主要差别就是此shell是放在电脑上运行,5是push在机器上运行
#!/bin/bash
echo "test....Pid: $$"
Display1Event=/dev/input/event0
Display2Event=/dev/input/event1
Display3Event=/dev/input/event2
#swipe from rigint to left
swipeRightToLeft(){
XstartCoord=1900
YstartCoord=300
interval=200
XendCoord=800
adb shell sendevent $Display1Event 3 57 33
adb shell sendevent $Display1Event 3 53 $XstartCoord
adb shell sendevent $Display1Event 3 54 $YstartCoord
adb shell sendevent $Display1Event 1 330 1
adb shell sendevent $Display1Event 0 0 0
echo "swipeRightToLeft....down"
while [ `expr $XstartCoord - $interval` -gt $XendCoord ]; do
echo "swipeRightToLeft...move"
XstartCoord=`expr $XstartCoord - $interval`
adb shell sendevent $Display1Event 3 53 $XstartCoord
adb shell sendevent $Display1Event 3 54 $YstartCoord
adb shell sendevent $Display1Event 0 0 0
done
echo "swipeRightToLeft....up"
adb shell sendevent $Display1Event 3 57 ffffffff
#adb shell sendevent $Display1Event 3 57 0
adb shell sendevent $Display1Event 1 330 0
adb shell sendevent $Display1Event 0 0 0
}
#swipe from rigint to left
swipeLeftToRight(){
XstartCoord=10 #down的x坐标
YstartCoord=300
interval=200
XendCoord=1000 #结束时坐标
adb shell sendevent $Display1Event 3 57 33
adb shell sendevent $Display1Event 3 53 $XstartCoord
adb shell sendevent $Display1Event 3 54 $YstartCoord
adb shell sendevent $Display1Event 1 330 1
adb shell sendevent $Display1Event 0 0 0
echo "swipeRightToLeft....down"
while [ `expr $XstartCoord + $interval` -lt $XendCoord ]; do
echo "swipeRightToLeft...move"
XstartCoord=`expr $XstartCoord + $interval`
adb shell sendevent $Display1Event 3 53 $XstartCoord
adb shell sendevent $Display1Event 3 54 $YstartCoord
adb shell sendevent $Display1Event 0 0 0
done
echo "swipeRightToLeft....up"
adb shell sendevent $Display1Event 3 57 ffffffff
#adb shell sendevent $Display1Event 3 57 0
adb shell sendevent $Display1Event 1 330 0
adb shell sendevent $Display1Event 0 0 0
}
adb root
adb remount
echo $Display1Event
while true; do
echo "while...."
swipeRightToLeft
sleep 1
swipeLeftToRight
sleep 1
done
echo "test finish...."
4. python脚本连续发送滑动事件
电脑端python脚本可用于window和linux测试
import sys
import os
import time
import datetime
print ('usage: test.py path [excel path]')
# 1. adb shell命令方法有好几种,可以google
def adb_shell(cmd):
exit_code = os.system(cmd)
return exit_code>>8
# 2. 添加了adb shell sendevent而已
def adb_shell_sendevent(cmd):
exit_code = os.system("adb shell sendevent " + cmd)
return exit_code>>8
# 3. 滑动的方法
def swipeRightToLeft(displayEvent) :
begin = datetime.datetime.now()
DisplayEvent=displayEvent
XstartCoord=1900
YstartCoord=300
interval=200
XendCoord=800
# 3.1 发送xy坐标 + down事件
adb_shell_sendevent(DisplayEvent + " 3 57 33 ")
adb_shell_sendevent(DisplayEvent + " 3 53 " + str(XstartCoord))
adb_shell_sendevent(DisplayEvent + " 3 54 " + str(YstartCoord))
adb_shell_sendevent(DisplayEvent + " 1 330 1")
adb_shell_sendevent(DisplayEvent + " 0 0 0")
# 3.2 发送move xy坐标
#print ("swipeRightToLeft....down")
while ( XstartCoord - interval > XendCoord ) :
XstartCoord=XstartCoord - interval
adb_shell_sendevent(DisplayEvent + " 3 53 " + str(XstartCoord))
adb_shell_sendevent(DisplayEvent + " 3 54 " + str(YstartCoord))
adb_shell_sendevent(DisplayEvent + " 0 0 0")
# 3.3 发送up
#print ("swipeRightToLeft....up")
adb_shell_sendevent(DisplayEvent + " 3 57 ffffffff")
adb_shell_sendevent(DisplayEvent + " 1 330 0 ")
adb_shell_sendevent(DisplayEvent + " 0 0 0")
end = datetime.datetime.now()
print("swipeRightToLeft Time: ",end-begin)
def swipeLeftToRight(displayEvent) :
begin = datetime.datetime.now()
DisplayEvent=displayEvent
XstartCoord=10 #down的x坐标
YstartCoord=300
interval=200
XendCoord=1000 #结束时坐标
adb_shell_sendevent(DisplayEvent + " 3 57 33 ")
adb_shell_sendevent(DisplayEvent + " 3 53 " + str(XstartCoord))
adb_shell_sendevent(DisplayEvent + " 3 54 " + str(YstartCoord))
adb_shell_sendevent(DisplayEvent + " 1 330 1")
adb_shell_sendevent(DisplayEvent + " 0 0 0")
#print ("swipeLeftToRight....down")
while ( XstartCoord + interval < XendCoord ) :
XstartCoord=XstartCoord + interval
adb_shell_sendevent(DisplayEvent + " 3 53 " + str(XstartCoord))
adb_shell_sendevent(DisplayEvent + " 3 54 " + str(YstartCoord))
adb_shell_sendevent(DisplayEvent + " 0 0 0")
#print ("swipeLeftToRight....up")
adb_shell_sendevent(DisplayEvent + " 3 57 ffffffff")
adb_shell_sendevent(DisplayEvent + " 1 330 0 ")
adb_shell_sendevent(DisplayEvent + " 0 0 0")
end = datetime.datetime.now()
print("swipeRightToLeft Time: ",end-begin)
#main函数
def main(argv):
adb_shell("adb root && adb remount")
Display0Event="/dev/input/event0"
Display1Event="/dev/input/event1"
Display2Event="/dev/input/event2"
#adb_shell(cmd)
while 0:
#0-->1
swipeLeftToRight(Display0Event)
time.sleep(0.8)
#1-->2
swipeLeftToRight(Display1Event)
time.sleep(0.8)
#2-->1
swipeRightToLeft(Display2Event)
time.sleep(0.8)
#1-->0
swipeRightToLeft(Display1Event)
time.sleep(0.8)
while 1:
swipeLeftToRight(Display0Event)
time.sleep(0.8)
swipeRightToLeft(Display0Event)
time.sleep(0.8)
#函数主入口,其他Module导入不会走main
if __name__ == "__main__":
main(sys.argv[1:])
5. push shell到机器里
sendEvent在adb shell里面发,需要push到机器,避免电脑操作系统影响
#!/bin/bash
#获取pid进程号
echo "test....Pid: $$"
Display0Event=/dev/input/event0
Display1Event=/dev/input/event1
Display2Event=/dev/input/event2
#swipe from rigint to left
swipeRightToLeft(){
DisplayEvent=$1
XstartCoord=1900
YstartCoord=300
interval=200
XendCoord=800
start=$(date +%s)
#1. 发送down事件,和初始xy坐标,最后report
sendevent $DisplayEvent 3 57 33
sendevent $DisplayEvent 3 53 $XstartCoord
sendevent $DisplayEvent 3 54 $YstartCoord
sendevent $DisplayEvent 1 330 1
sendevent $DisplayEvent 0 0 0
echo "swipeRightToLeft....down"
while [ `expr $XstartCoord - $interval` -gt $XendCoord ]; do
echo "swipeRightToLeft...move"
# 2. while循环发送move事件,发送x,y坐标加report就可以。这里是把y值每次减$interval
XstartCoord=`expr $XstartCoord - $interval`
sendevent $DisplayEvent 3 53 $XstartCoord
sendevent $DisplayEvent 3 54 $YstartCoord
sendevent $DisplayEvent 0 0 0
done
# 3. 最后发送up事件并report,完成一次提交
echo "swipeRightToLeft....up"
sendevent $DisplayEvent 3 57 ffffffff
#sendevent $DisplayEvent 3 57 0
sendevent $DisplayEvent 1 330 0
sendevent $DisplayEvent 0 0 0
# 4. 最后判断此次滑动事件,adb shell sendEvent耗时很久,大概2秒钟
end=$(date +%s)
take=$(( end - start ))
echo Time taken is ${take} seconds.
}
#swipe from rigint to left
swipeLeftToRight(){
DisplayEvent=$1
XstartCoord=10 #down的x坐标
YstartCoord=300
interval=200
XendCoord=1000 #结束时坐标
sendevent $DisplayEvent 3 57 33
sendevent $DisplayEvent 3 53 $XstartCoord
sendevent $DisplayEvent 3 54 $YstartCoord
sendevent $DisplayEvent 1 330 1
sendevent $DisplayEvent 0 0 0
echo "swipeRightToLeft....down"
while [ `expr $XstartCoord + $interval` -lt $XendCoord ]; do
echo "swipeRightToLeft...move"
XstartCoord=`expr $XstartCoord + $interval`
sendevent $DisplayEvent 3 53 $XstartCoord
sendevent $DisplayEvent 3 54 $YstartCoord
sendevent $DisplayEvent 0 0 0
done
echo "swipeRightToLeft....up"
sendevent $DisplayEvent 3 57 ffffffff
#sendevent $DisplayEvent 3 57 0
sendevent $DisplayEvent 1 330 0
sendevent $DisplayEvent 0 0 0
}
adb root
adb remount
echo $Display1Event
while false; do
echo "while...."
#0-->1
swipeLeftToRight Display0Event
sleep 1
#1-->2
swipeLeftToRight Display1Event
sleep 1
#2-->1
swipeRightToLeft Display2Event
sleep 1
#1-->0
swipeRightToLeft Display1Event
sleep 1
done
#左滑,右滑依次判断
while true; do
echo "while...."
swipeLeftToRight $Display0Event
sleep 1
swipeRightToLeft $Display0Event
sleep 1
done
echo "test finish...."
6. 问题
6.1. sendEvent命令滑动卡顿
因为我这里,设置的间隔坐标很大,同时sendevent到inputdispacher又到界面,太慢了,耗时太久
6.2. sendevent耗时太久
sendEvetn是软件模拟,input命令是调用inputdispatcher感觉也有点慢
6.3. Input缺点
input默认是0的DisplayID,在多屏中好像没法使用,但是又不想去看binder接口,如果可以,建议改inputManagerService的injectInputEvent看效果
7. 修改sendevent解决 adb shell sendevent慢的问题
7.1 adb shell sendevent慢的原因
adb shell sendevent是通过toybox里面的sendevent来完成event的发送
每次调用sendevent都会走main开启一个进程,从getpid log就可以看出来。没fork一个进程是很耗费时间的,同时main函数是先open,再read,重复的open也是很耗时的
@sendevent.c
void sendevent_main(void)
{
error_msg("william sendevent_main pid: %d ", getpid());
int fd = xopen(*toys.optargs, O_RDWR);
int version;
struct input_event ev;
if (ioctl(fd, EVIOCGVERSION, &version))
perror_exit("EVIOCGVERSION failed for %s", *toys.optargs);
memset(&ev, 0, sizeof(ev));
// TODO: error checking and support for named constants.
ev.type = atoi(toys.optargs[1]);
ev.code = atoi(toys.optargs[2]);
ev.value = atoi(toys.optargs[3]);
xwrite(fd, &ev, sizeof(ev));
}
7.2 新建命令sendeventtsp来替代脚本实现滑动
external/toybox 目录中修改和添加的文件如下
@external/toybox$
修改: Android.mk
修改: generated/config.h
修改: generated/flags.h
修改: generated/help.h
修改: generated/newtoys.h
修改: toys/android/sendevent.c
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
toys/android/sendeventtsp.c
编译后需要push的文件如下
adb root
adb remount
adb push ./system/bin/toybox /system/bin/toybox
adb push ./vendor/bin/toybox_vendor /vendor/bin/toybox_vendor
adb push ./vendor/bin/sendeventtsp /vendor/bin/sendeventtsp
adb push ./system/bin/sendeventtsp /system/bin/sendeventtsp
git diff修改后的patch如下。里面主要是需要配置tsp,需要toybox把sendeventtsp_main()z注册到命令里面去
diff --git a/Android.mk b/Android.mk
index a2cfe5f..50d7a1b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -64,6 +64,7 @@ common_SRC_FILES := \
toys/android/restorecon.c \
toys/android/runcon.c \
toys/android/sendevent.c \
+ toys/android/sendeventtsp.c \
toys/android/setenforce.c \
toys/android/setprop.c \
toys/android/start.c \
@@ -339,6 +340,7 @@ ALL_TOOLS := \
runcon \
sed \
sendevent \
+ sendeventtsp \
seq \
setenforce \
setprop \
diff --git a/generated/config.h b/generated/config.h
index e6f9354..d59d913 100644
--- a/generated/config.h
+++ b/generated/config.h
@@ -456,6 +456,8 @@
#define USE_SED(...) __VA_ARGS__
#define CFG_SENDEVENT 1
#define USE_SENDEVENT(...) __VA_ARGS__
+#define CFG_SENDEVENTTSP 1
+#define USE_SENDEVENTTSP(...) __VA_ARGS__
#define CFG_SEQ 1
#define USE_SEQ(...) __VA_ARGS__
#define CFG_SETENFORCE 1
diff --git a/generated/flags.h b/generated/flags.h
index 431b924..4a937ff 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -2227,6 +2227,14 @@
#undef FOR_sendevent
#endif
+// sendeventtsp <4>4 <4>4
+#undef OPTSTR_sendeventtsp
+#define OPTSTR_sendeventtsp "<4>4"
+#ifdef CLEANUP_sendeventtsp
+#undef CLEANUP_sendeventtsp
+#undef FOR_sendeventtsp
+#endif
+
// seq <1>3?f:s:w[!fw] <1>3?f:s:w[!fw]
#undef OPTSTR_seq
#define OPTSTR_seq "<1>3?f:s:w[!fw]"
@@ -5091,6 +5099,13 @@
#endif
#endif
+#ifdef FOR_sendeventtsp
+#ifndef TT
+#define TT this.sendeventtsp
+#endif
+#endif
+
+
#ifdef FOR_seq
#ifndef TT
#define TT this.seq
diff --git a/generated/help.h b/generated/help.h
index 811b534..145a96e 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -44,6 +44,8 @@
#define HELP_sendevent "usage: sendevent DEVICE TYPE CODE VALUE\n\nSends a Linux input event.\n\n"
+#define HELP_sendeventtsp "usage: sendeventtsp DEVICE TYPE CODE VALUE\n\nSends a Linux input event.\n\n"
+
#define HELP_runcon "usage: runcon CONTEXT COMMAND [ARGS...]\n\nRun a command in a specified security context.\n\n"
#define HELP_restorecon "usage: restorecon [-D] [-F] [-R] [-n] [-v] FILE...\n\nRestores the default security contexts for the given files.\n\n-D apply to /data/data too\n-F force reset\n-R recurse into directories\n-n don't make any changes; useful with -v to see what would change\n-v verbose: show any changes\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index 46cc2db..5b7920f 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -197,6 +197,7 @@ USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SED(NEWTOY(sed, "(help)(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_SENDEVENTTSP(NEWTOY(sendeventtsp, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/toys/android/sendevent.c b/toys/android/sendevent.c
index 8e982e0..e2b427a 100644
--- a/toys/android/sendevent.c
+++ b/toys/android/sendevent.c
@@ -21,6 +21,8 @@ config SENDEVENT
void sendevent_main(void)
{
+ error_msg("william sendevent_main pid: %d ", getpid());
+
int fd = xopen(*toys.optargs, O_RDWR);
int version;
struct input_event ev;
在adb shell 里面敲 sendeventtsp 0 0 0 0 就会执行main()函数,就会循环滑动
/* sendevent.c - Send Linux input events.
*
* Copyright 2016 The Android Open Source Project
USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
config SENDEVENT
bool "sendevent"
default y
depends on TOYBOX_ON_ANDROID
help
usage: sendevent DEVICE TYPE CODE VALUE
Sends a Linux input event.
*/
#define FOR_sendeventtsp
#include "toys.h"
#include <linux/input.h>
#include <time.h>
int fd0event = 0;
struct input_event ev;
clock_t begin, end;
double cost;
void writeEvent(int fd, int type, int code, int value ) {
ev.type = type;
ev.code = code;
ev.value = value;
xwrite(fd, &ev, sizeof(ev));
}
void swipeLeftToRight(char* displayEventID) {
begin = clock();
int XstartCoord = 10;
int YstartCoord = 300;
int interval = 80;
int XendCoord = 1200;
int fdevent = fd0event;
writeEvent(fdevent, 3, 57, 33 );
writeEvent(fdevent, 3, 53, XstartCoord );
writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 1, 330, 1 );
writeEvent(fdevent, 0, 0, 0 );
while ( XstartCoord + interval < XendCoord ) {
XstartCoord = XstartCoord + interval;
writeEvent(fdevent, 3, 53, XstartCoord );
//writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 0, 0, 0 );
}
writeEvent(fdevent, 3, 57, 0xffffffff );
writeEvent(fdevent, 1, 330, 0 );
writeEvent(fdevent, 0, 0, 0 );
end = clock();
cost = (double)(end - begin)/CLOCKS_PER_SEC;
error_msg("swipeLeftToRight() time cost is: %lf secs", cost);
}
void swipeRightToLeft(char* displayEventID) {
begin = clock();
int XstartCoord = 1900;
int YstartCoord = 300;
int interval = 80;
int XendCoord = 700;
int fdevent = fd0event;
writeEvent(fdevent, 3, 57, 33 );
writeEvent(fdevent, 3, 53, XstartCoord );
writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 1, 330, 1 );
writeEvent(fdevent, 0, 0, 0 );
while ( XstartCoord - interval > XendCoord ) {
XstartCoord = XstartCoord - interval;
writeEvent(fdevent, 3, 53, XstartCoord );
//writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 0, 0, 0 );
}
writeEvent(fdevent, 3, 57, 0xffffffff );
writeEvent(fdevent, 1, 330, 0 );
writeEvent(fdevent, 0, 0, 0 );
end = clock();
cost = (double)(end - begin)/CLOCKS_PER_SEC;
error_msg("swipeRightToLeft() time cost is: %lf secs", cost);
}
void sendeventtsp_main(void)
{
int fd = xopen("/dev/input/event0", O_RDWR);
fd0event = fd;
int version;
if (ioctl(fd, EVIOCGVERSION, &version))
perror_exit("EVIOCGVERSION failed for");
memset(&ev, 0, sizeof(ev));
// TODO: error checking and support for named constants.
while(1) {
swipeRightToLeft(NULL);
sleep(1);
swipeRightToLeft(NULL);
sleep(1);
swipeLeftToRight(NULL);
sleep(1);
swipeLeftToRight(NULL);
sleep(1);
}
}
7.3 sendeventtsp结果
sendeventtsp结果如下,可以看出每次滑动的时间是毫秒级,而如果使用adb shell sendeventt需要几秒钟,这差距就很大,这里的滑动还可以像input那样控制每次move的时间,很方便使用。
msm8996_gvmq:/ # sendeventtsp 0 0 0 0
sendeventtsp: swipeRightToLeft() time cost is: 0.000211 secs
sendeventtsp: swipeRightToLeft() time cost is: 0.000193 secs
sendeventtsp: swipeLeftToRight() time cost is: 0.000204 secs
sendeventtsp: swipeLeftToRight() time cost is: 0.000183 secs
sendeventtsp: swipeRightToLeft() time cost is: 0.000212 secs
sendeventtsp: swipeRightToLeft() time cost is: 0.000220 secs
sendeventtsp: swipeLeftToRight() time cost is: 0.000210 secs
7.4 在sendevent.c添加逻辑
不新建命令,直接在原有命令添加个额外的逻辑就可以了。
使用方法sendevent 0 100 100 100
@sendevent.c
#define FOR_sendevent
#include "toys.h"
#include <linux/input.h>
#include <time.h>
int fd0event = 0;
struct input_event ev;
clock_t begin, end;
double cost;
void writeEvent(int fd, int type, int code, int value ) {
ev.type = type;
ev.code = code;
ev.value = value;
xwrite(fd, &ev, sizeof(ev));
}
void swipeLeftToRight(char* displayEventID) {
begin = clock();
int XstartCoord = 10;
int YstartCoord = 300;
int interval = 80;
int XendCoord = 1200;
int fdevent = fd0event;
writeEvent(fdevent, 3, 57, 33 );
writeEvent(fdevent, 3, 53, XstartCoord );
writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 1, 330, 1 );
writeEvent(fdevent, 0, 0, 0 );
while ( XstartCoord + interval < XendCoord ) {
XstartCoord = XstartCoord + interval;
writeEvent(fdevent, 3, 53, XstartCoord );
//writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 0, 0, 0 );
}
writeEvent(fdevent, 3, 57, 0xffffffff );
writeEvent(fdevent, 1, 330, 0 );
writeEvent(fdevent, 0, 0, 0 );
end = clock();
cost = (double)(end - begin)/CLOCKS_PER_SEC;
error_msg("swipeLeftToRight() time cost is: %lf secs", cost);
}
void swipeRightToLeft(char* displayEventID) {
begin = clock();
int XstartCoord = 1900;
int YstartCoord = 300;
int interval = 80;
int XendCoord = 700;
int fdevent = fd0event;
writeEvent(fdevent, 3, 57, 33 );
writeEvent(fdevent, 3, 53, XstartCoord );
writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 1, 330, 1 );
writeEvent(fdevent, 0, 0, 0 );
while ( XstartCoord - interval > XendCoord ) {
XstartCoord = XstartCoord - interval;
writeEvent(fdevent, 3, 53, XstartCoord );
//writeEvent(fdevent, 3, 54, YstartCoord );
writeEvent(fdevent, 0, 0, 0 );
}
writeEvent(fdevent, 3, 57, 0xffffffff );
writeEvent(fdevent, 1, 330, 0 );
writeEvent(fdevent, 0, 0, 0 );
end = clock();
cost = (double)(end - begin)/CLOCKS_PER_SEC;
error_msg("swipeRightToLeft() time cost is: %lf secs", cost);
}
void sendevent_main(void)
{
error_msg("william sendevent_main pid: %d ", getpid());
if(atoi(toys.optargs[1]) == 100 && atoi(toys.optargs[2]) == 100 && atoi(toys.optargs[3]) == 100) {
int fd = xopen("/dev/input/event0", O_RDWR);
fd0event = fd;
int version;
if (ioctl(fd, EVIOCGVERSION, &version))
perror_exit("EVIOCGVERSION failed for");
memset(&ev, 0, sizeof(ev));
// TODO: error checking and support for named constants.
while(1) {
swipeRightToLeft(NULL);
sleep(1);
swipeRightToLeft(NULL);
sleep(1);
swipeLeftToRight(NULL);
sleep(1);
swipeLeftToRight(NULL);
sleep(1);
}
}
int fd = xopen(*toys.optargs, O_RDWR);
int version;
struct input_event ev;
if (ioctl(fd, EVIOCGVERSION, &version))
perror_exit("EVIOCGVERSION failed for %s", *toys.optargs);
memset(&ev, 0, sizeof(ev));
// TODO: error checking and support for named constants.
ev.type = atoi(toys.optargs[1]);
ev.code = atoi(toys.optargs[2]);
ev.value = atoi(toys.optargs[3]);
xwrite(fd, &ev, sizeof(ev));
}
这种解决了添加命令在Andrid各版本上的差异,修改会更少,但是容易影响原有功能。
8. toybox sendevent源码解析
从mk开始看,主要是生成system/bin/toybox, vendor/bin/toybox_vendor,/recovery/root/sbin/toybox_static(static没看到镜像里有)
其中LOCAL_POST_INSTALL_CMD会在install后,ln -sf建立软连接,可以通过ls -l查看,软连接对象ALL_TOOLS是在前面定义的各个名称,包含sendevent
@external/toybox/Android.mk
############################################
# toybox for /system
############################################
include $(CLEAR_VARS)
LOCAL_MODULE := toybox
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_SHARED_LIBRARIES := $(toybox_libraries)
# This doesn't actually prevent us from dragging in libc++ at runtime
# because libnetd_client.so is C++.
LOCAL_CXX_STL := none
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toybox $(TARGET_OUT)/bin/$(t);)
include $(BUILD_EXECUTABLE)
############################################
# toybox for /vendor
############################################
include $(CLEAR_VARS)
LOCAL_MODULE := toybox_vendor
LOCAL_VENDOR_MODULE := true
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_STATIC_LIBRARIES := libcutils libcrypto libz
LOCAL_SHARED_LIBRARIES := libselinux_vendor liblog
LOCAL_MODULE_TAGS := optional
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf ${LOCAL_MODULE} $(TARGET_OUT_VENDOR_EXECUTABLES)/$(t);)
include $(BUILD_EXECUTABLE)
############################################
# static version to be installed in recovery
############################################
include $(CLEAR_VARS)
LOCAL_MODULE := toybox_static
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_CFLAGS := $(common_CFLAGS)
LOCAL_STATIC_LIBRARIES := $(toybox_libraries)
# libc++_static is needed by static liblog
LOCAL_CXX_STL := libc++_static
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf ${LOCAL_MODULE} $(LOCAL_MODULE_PATH)/$(t);)
include $(BUILD_EXECUTABLE)
可以看到setenforce, setprop, sendevent等都是toybox的软连接
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 sed -> toybox
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 sendevent -> toybox
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 sendeventtsp -> toybox
-rwxrwxr-x 1 wangdong wangdong 11096 12月 3 13:14 sensorservice
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 seq -> toybox
-rwxrwxr-x 1 wangdong wangdong 36056 12月 3 13:14 service
-rwxrwxr-x 1 wangdong wangdong 19688 12月 3 13:14 servicemanager
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 setenforce -> toybox
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 setprop -> toybox
lrwxrwxrwx 1 wangdong wangdong 6 12月 23 14:14 setsid -> toybox
实践调用应该是这样,软连接可以直接 sendeventtsp 0 0 0 0
msm8996_gvmq:/ # toybox sendeventtsp 0 0 0 0
sendeventtsp: swipeRightToLeft() time cost is: 0.000215 secs
sendeventtsp: swipeRightToLeft() time cost is: 0.000200 secs
sendeventtsp: swipeLeftToRight() time cost is: 0.000394 secs
sendeventtsp: swipeLeftToRight() time cost is: 0.000195 secs