Android Input和SendEvent脚本命令模拟连续滑动事件

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
  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值