In daily tests, we often need to simulate user clicks and other operations to achieve various input functions of simulated users. Here, we summarize several click methods, as well as their respective advantages and disadvantages. At present, there are roughly four ways to achieve cross-process clicks

  • Adb shell command input, we will see it supports the following event types:


private BufferedInputStream stdin = null;
private BufferedOutputStream stdout = null;
private BufferedInputStream erroeOut = null;
private static Process mProcess = null;

public Shell(String su) throws IOException, InterruptedException {

  mProcess = Runtime.getRuntime().exec(su);
  this.stdin = new BufferedInputStream(mProcess.getInputStream());
  this.stdout = new BufferedOutputStream(mProcess.getOutputStream());
  this.erroeOut = new BufferedInputStream(mProcess.getErrorStream());
  this.read();
}

public void write(String value) throws IOException {
  this.stdout.write((value + "\n").getBytes());
  this.stdout.flush();
}Copy the code

Above is a simple partition of executing shell commands, now we just pass in the corresponding command, for example, we click the point (100,200) on the screen

Shell shell = new Shell("su");
shell.write( "input tap 100 200" );Copy the code

Adb shell Input is relatively simple to use, but it is slow to execute.

  • 2: monkey

    You know Monkey used for pressure test, in fact, when we run the Monkey will start “com.android.com mands. Monkey” such a process, all sorts of Monkey click slide operation is implemented in the process, so if we can send events to the process, So now that we have the ability to click across processes, let’s see how we can do that.

    Start “com.android.com mands. Monkey” process, can be use the monkey – port 3131, monkey process starts, how can communication can, in fact, we can through the way of the socket to establish a connection, You can communicate with the Monkey process by connecting the 127.0.0.1 address and port 3131, so let’s do that


    First check if the Monkey process is running, then kill it and start it again.

    Now we can create a socket that connects to port 3131, which is the monkey process we just started.

    After connecting to the Monkey process, we can send click events. The Monkey click events have two parts: Down and Up. The format is as follows:

    touch down x y
    touch up x yCopy the code

    It can not only send click events, but also send keycode and other events. Monkey execution efficiency is much faster, but sometimes its stability is not good enough, and it and UiAutomator can not be used at the same time, the two conflict

  • Three: Instrumentation:

    If we can send a click event in our own app in the following way,
    long downTime = SystemClock.uptimeMillis();
    MotionEvent tapDownEvent = MotionEvent.obtain(downTime, downTime,MotionEvent.ACTION_DOWN, x, y, 0);
    MotionEvent tapUpEvent = MotionEvent.obtain(downTime+100,downTime+100, MotionEvent.ACTION_UP, x, y, 0);
    mInstrumentation.sendPointerSync(tapDownEvent);
    mInstrumentation.sendPointerSync(tapUpEvent);
    tapDownEvent.recycle();
    tapUpEvent.recycle();Copy the code

    However, if you want a cross-process click, it will report this error,Permission denied, home event from….

    This error is caused by sending click events from one application to another, and the two applications have different UUIds.

    Therefore, there are two ways to achieve cross-process capability. One is to hook the process of the application under test and click in the application under test through process communication. Another way is to hook the UID of the test application

    Native InjectInputEvent in hook, change the UID value to 0, so it can pass the permission verification of hasInjectPermission inside.

    findAndHookMethod("com.android.server.input.InputManagerService", lpparam.classLoader,
                      "nativeInjectInputEvent", int.class, lpparam.classLoader.loadClass("android.view.InputEvent"),
                      int.class, int.class, int.class, int.class, int.class, new XC_MethodHook() {
                          @Override
                          protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                              XposedBridge.log("lzf called nativeInjectInputEvent:" + param.args[3]);
                              XposedBridge.log("uid is :" + TargetUid + "" + param.args[3]);
                              if ((Integer) param.args[3] == TargetUid) {
                                  XposedBridge.log("here:" + param.args[3]);
                                  param.args[3] = 0; }}});Copy the code

    TargetUid is the UID of the application we want to inject,

    More detailed reasons are availablewww.hizher.com/pageContent…

  • Iv. Sendevent Mode:

    Android provides these two handy tools to handle input events

    Getevent: Displays the input information of the device

    Sendevent: Injects input events

    Get input information from the device using getevent:


    This is the information on the N5 device (different devices may have different information). You can see that there are 6 event devices in the N5 phone. /dev/inpu/event1 is the device that sends the touch

    /dev/input/event1: EV_ABS       ABS_MT_TRACKING_ID   00000005
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    0000018b
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000529
    /dev/input/event1: EV_ABS       ABS_MT_PRESSURE      00000030
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    0000018a
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000528
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    00000189
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000527
    /dev/input/event1: EV_ABS       ABS_MT_PRESSURE      0000002e
    /dev/input/event1: EV_ABS       ABS_MT_TOUCH_MAJOR   00000003
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_TRACKING_ID   ffffffff
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000Copy the code

    Take a look at the description of each item above:

    ABS_MT_TRACKING_ID: ID for reporting touch tracking, is a non-negative, arbitrary integer used to distinguish multiple simultaneous operations. For example, when multiple fingers touch the device, each finger is bound to a separate tracking ID while the finger is on the screen, and the tracking ID may be reused when the finger is off the screen (optional)

    ABS_MT_POSITION_X: X-axis coordinates of touch events (mandatory)

    ABS_MT_POSITION_Y: Y coordinates of touch events (mandatory)

    ABS_MT_PRESSURE: Touch event pressure or signal strength (optional)

    SYN_REPORT: Semaphore sent when the touch event is up

    More references:Multi-touch devices use the following Linux input events

    Each Sendevent command requires four parameters

    device_name (string)

    event_type (decimal int)

    event_code (decimal int)

    value (decimal int)

    The coordinates above are hexadecimal converted to decimal after the click event is sent

    sendevent /dev/input/event1 3 53 395
    sendevent /dev/input/event1 3 54 1321
    sendevent /dev/input/event1 0 0 0Copy the code

    In this mode, the event types of each device are different and need to be matched with different models.