Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android simulate fast swipe

Tags:

android

adb

I am doing a generic automation script.

I need to send complex swipe events to the android screen without specifically having access to the focused application(s)

Best way I figured so far is to use adb, create a file with sendevent commands, push it on the device and run it from there. Even that, it is painfully slow (much slower compared to if I record it with getevent and pipe it back in).

I managed to optimize the file since I figured out that each sendevent block does not specifically require both X and Y, but it is still a few orders of magnitude slower

Example of part of the file (I'm trying on a HTC One):

sendevent /dev/input/event5 3 57 49
sendevent /dev/input/event5 3 53 942
sendevent /dev/input/event5 3 54 2747
sendevent /dev/input/event5 0 0 0

sendevent /dev/input/event5 3 53 1207
sendevent /dev/input/event5 3 54 2483
sendevent /dev/input/event5 0 0 0

sendevent /dev/input/event5 3 53 1472
sendevent /dev/input/event5 0 0 0

sendevent /dev/input/event5 3 54 2218
sendevent /dev/input/event5 0 0 0

sendevent /dev/input/event5 3 53 1207
sendevent /dev/input/event5 3 54 2483
sendevent /dev/input/event5 0 0 0

sendevent /dev/input/event5 3 53 1472

So my focus is to optimize the speed of single long-complex swipes, not of multiple small ones.

Anyone know of a better way to do this?


So, Chris Stratton's idea worked in principle (re-piping the cat-ed output generates the same swipe successfully), but I can't be able to create my own code to pipe it back in. I'm guessing it's something to do with the separators between send event commands... but I still can't get it to work

I used a modification of the sendevent.c file to get a file with triples per line and output to another file. Do you happen to know what could be the issue? Conversion looks good ...


SOLLUTION: I managed to solve it, mostly thanks to the answers bellow. Here is a C script that takes a file with HEX values and outputs the appropriate binary file.

Usage: (for me the touch driver file is /dev/input/event5 - HTC One - for other devices it might be a different file !!!)

 $> adb shell getevent > tmp.in
 $> ./sendevent tmp.in tmp.out
 $> adb shell push tmp.out /mnt/sdcard/
 $> adb shell "cd /mnt/sdcard/ && cat tmp.out > /dev/input/event5"

and the source:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>

typedef uint32_t        __u32;
typedef uint16_t        __u16;
typedef __signed__ int  __s32;

__attribute__((aligned(1),packed)) struct input_event {
    __u32 time_dummy_1;
    __u32 time_dummy_2;
    __u16 type;
    __u16 code;
    __s32 value;
};

int convert (char * str) {
    return (int) strtol(str, NULL, 16);
}

#define S_ALL (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)

int main (int argc, char *argv[]) {
    int i;
    int fd;
    int ret;

    if(argc < 3) {
        fprintf(stderr, "use: %s in-file out-file\n", argv[0]);
        return 1;
    }

    fd = open(argv[2], O_CREAT | O_WRONLY, S_ALL);
    if(fd < 0) {
        fprintf(stderr, "could not open %s, %s\n", argv[2], strerror(errno));
        return 1;
    }

    FILE * fd_in = fopen(argv[1], "r");
    if (fd_in == NULL) {
        fprintf(stderr, "Can't open input file: %s\n", argv[1]);
        return 1;
    }

    struct input_event event;
    char type[32];
    char code[32];
    char value[32];
    int count = 0;
    while (fscanf(fd_in, "%s %s %s", type, code, value) != EOF) {
        memset(&event, 0, sizeof(event));
        // printf("%d) %s %s %s\n", ++count, type, code, value);
        event.type = convert(type);
        event.code = convert(code);
        event.value = convert(value);
        memset(type, 0, sizeof(type));
        memset(code, 0, sizeof(code));
        memset(value, 0, sizeof(value));
        ret = write(fd, &event, sizeof(event));
        if(ret < sizeof(event)) {
            fprintf(stderr, "write event failed, %s\n", strerror(errno));
            return -1;
        }
    }

    return 0;
}
like image 409
Stefan Avatar asked Sep 28 '13 03:09

Stefan


2 Answers

Please note that this answer pertains to circa-2013 versions of Android and may not apply to current ones. Jellybean was contemporary at the time, Kitkat came out a few weeks after the question was asked

Your delay is likely a result of inefficiently having to repeatedly launch a new sendevent process, parse the textual event record, and open the device node - for each individual event. If you instead do everything from within a single process, opening the device file only once, it will be much more efficient.

If we look at the source for sendevent in toolbox contemporary with the date of the question (for example, https://android.googlesource.com/platform/system/core/+/jb-release/toolbox/sendevent.c ) we see that the core of what it is doing is encoding the events into binary records

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

and writing them to the appropriate device

memset(&event, 0, sizeof(event));
event.type = atoi(argv[2]);
event.code = atoi(argv[3]);
event.value = atoi(argv[4]);
ret = write(fd, &event, sizeof(event));

Provided that you are executing something as the shell userid or another in the input unix group, you should be able to accomplish the same thing that sendevent does from your own custom program, or using other command line tools like cat, thus efficiently pushing a binary file of event records.

For example

adb shell
cd /mnt/sdcard
cat /dev/input/event2 > events

Do a few touch screen events, then ctrl-C to kill cat

Now you can play back the captured file of binary events:

cat events > /dev/input/event2 

(Note: sendevent is zeroing the timeval part of each record; recording and playback may not do that; you'll have to see, and if it matters zero those portions of each record from the file before you write it back)

like image 159
Chris Stratton Avatar answered Sep 19 '22 18:09

Chris Stratton


If you just want to produce linear swipes, you can use input swipe command on shell.

$ adb shell input
usage: input ...
       input text <string>
       input keyevent <key code number or name>
       input [touchscreen|touchpad|touchnavigation] tap <x> <y>
       input [touchscreen|touchpad|touchnavigation] swipe <x1> <y1> <x2> <y2> [duration(ms)]
       input trackball press
       input trackball roll <dx> <dy>

Command below draws a nice line for me in a drawing application

$ adb shell input swipe 300 300 500 1000

and a quicker one

$ adb shell input touchscreen swipe 300 300 500 1000 100
like image 42
auselen Avatar answered Sep 21 '22 18:09

auselen