android回放记录

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;
}
share improve this question
 
 
I know this is an old question, but would you be able to explain how to get this new sendevents.c file onto the android device, i presume you would use adb push but dont know what directory i can place the file on so that it is an executable. –  theellipsis  Apr 17 '14 at 10:35
 
I have now created a question as that is more appropriate. your help would be appreciated.stackoverflow.com/questions/23131081/… –  theellipsis  Apr 17 '14 at 10:48
 
I posted an answer to your question. Hope it helps: stackoverflow.com/questions/23131081/… –  Stefan  Apr 17 '14 at 14:25

2 Answers

up vote 11 down vote accepted
+250

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 (for example, from the unofficial but browsable mirror at https://github.com/android/platform_system_core/blob/master/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)

share improve this answer
 
 
that's seems like a great idea. lemme try it out and I'll get back to you –  Stefan  Oct 2 '13 at 16:44
 
it almost works, can you please look over my updated post, maybe you know what's wrong –  Stefan  Oct 2 '13 at 19:04
 
@tak3r - while I don't know if it's your only problem, it looks like you are using format specifiers for an int ie "%d" but passing a pointer to an __u16 (which is effectively an unsigned short) to fscanf. You probably want "%hu", or else read it into a temporary unsigned int and then copy it over. –  Chris Stratton  Oct 2 '13 at 19:16 
 
ok, changed that but still no effect. Not sure how significant it is, but for a random complex swipe if I record it with cat ... | wc it gives: 52 301 8640 in parallel I also register the adb getevent output, convert the values to base 10 and run ./sendevent on it after which the result is: 0 11 7704 The number of events recorded with sendevent is 321 which is quite close to the number of words reported by wc thats why (and also the fact that mine has no lines) i think it mainly has to do with some sort of separator as well –  Stefan  Oct 2 '13 at 20:02
 
Try running both on your dev machine against a regular file target and hexdumping the outputs to compare. Or just compare your input to the hexdump of the output. –  Chris Stratton  Oct 2 '13 at 20:05

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
share improve this answer
 
 
i need complex swipes unfortunately or if there are linear swipes then I need them to not have a keyup event at the end so i can compose them in a single complex swipe –  Stefan  Oct 2 '13 at 16:41
 
@tak3r do you have root access? –  auselen  Oct 2 '13 at 16:43
1 
I don't think root access will be required. This appears to be usable from the adb shell, meaning the shelluserid has permission to do whatever is needed. The input command is a script which uses app-process to launch input.java (unofficial repo github.com/android/platform_frameworks_base/blob/master/cmds/… ) which could presumably be replaced with a custom version similarly compiled and stored. And similarly invokedfrom the adb shell - you would not be able to do this from an app with having an end-run around the security model. –  Chris Stratton  Oct 2 '13 at 16:52 
 
@ChrisStratton Unfortunately no. Being under system directory make that application talk to components otherwise not possible. You can't even build that app outside of an Android repository, since it uses hidden APIs. –  auselen  Oct 2 '13 at 17:41
 
While it may use hidden APIs and be tricky to build, I don't believe the permission situation is as you think. For one it is not an "app" at all - it's bare dalvik/java code run by the shell user via the app_process tool–  Chris Stratton  Oct 2 '13 at 17:43 
 
 
Again, this code is not contained in an app, system or otherwise. So that does not apply. –  Chris Stratton Oct 2 '13 at 17:44 
 
@ChrisStratton I think your suggestion might be more fruitful in case you want to build your application, however you can't just push that app to the phone and execute. I just suggested this solution because it exists for linear swipes. –  auselen  Oct 2 '13 at 17:44
 
Again, this is not about an app or apk. It's been pointed out that your solution won't meet the needs of the question, what I'm suggesting in comments is that the mechanism behind your solution can probably be adapted to do what is needed. But you are hung up on app permissions, when this, not being an app, depends on the permissions associated with the unix user id shell rather than those of an app userid. –  Chris Stratton  Oct 2 '13 at 17:46 
 
@ChrisStratton let me investigate that. –  auselen  Oct 2 '13 at 18:06
1 
Hey guys. To answer a previous question: I don't have root yet (I don't think there is one out yet for HTC One). Currently I am only running this from an app on my mac, I push the script file with all the send events to the device and I run it from there and it works fine. I tried using adb shell and cat-ing into /dev/input/event5 (this is my touch events) and it worked. Check the updated post ^^ –  Stefan  Oct 2 '13 at 19:07
 
@ChrisStratton I made a few tries, not successful. Simply you end up with a jar and if you try to run it under AndroidRuntime (app_process) it needs to become an ODEX but that's not possible with shell user. If you want to just run a standalone jar via dalvikvm, then you are cut out from runtime. - Please educate me if you think there is some otherway. –  auselen  Oct 3 '13 at 9:43 
 
Hey this looks promising. Please answer this: How would you record what the user does on the screen (meaning the opposite of what is shown here) ? –  android developer  Mar 1 '14 at 0:15
 
If using mac terminal and firing command with touchscreen then it won't fire. Error "Error: Unknown command: touchscreen ". I need to simulate pinch zoom using terminal along with adb ,with provided coordinates. –  nikhil84  Aug 20 '14 at 10:10
 
@auselen can I generate a long touch using this method? I think I can use the swipe command with the same source and destination coordinates, is this possible? Also, what is the proper duration for normal touch and long one? Thanks. –  Dania  Mar 17 at 17:46
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值