4

I'm trying to automate a task on my (non-rooted) Android phone.

While I have it "working" using input, it is quite slow due to taking ~0.7s per event. (Basically, I just need to send a bunch of tap events, so what I have now is a sequence of adb shell input tap $x $y.)

I tried sendevent, but it gives me "permission denied". (I'm not sure why; the device is allegedly writable by the input group, of which the shell user is a member... and obviously, the input command can send events.)

Is there some trick to getting sendevent to work? Or some way to send multiple commands in a single invocation of input?

I'd prefer solutions that don't require installing third-party software...

3
  • I would recommend to edit your post and add more details on the task you want to automate. Based on what you have written it seems to be related to typing text, but the rest is not clear, which makes it difficult to write a good answer.
    – Robert
    Mar 30, 2021 at 16:26
  • @Robert, sorry... not text, just a bunch of tap events with known x,y coordinates. Basically, I just want to send a bunch of tap events to specific points, but it's really slow right now because input is slow.
    – Matthew
    Mar 30, 2021 at 17:00

2 Answers 2

2

So... really crappy method: fork adb... as in, run it on the local shell in the background, using &. This is super-ugly and somewhat dependent on each instance executing with consistent timing (and probably not too polite to the android device, asking it to spin up all those Java processes), but it works.

Example:

tap() { adb shell input tap "$@" & sleep 0.02 }
tap 500 500
tap 600 600
tap 700 700

...will send tap events to (500, 500), (600, 600) and (700, 700) in fairly quick succession.

0

EDIT: Below you can find a way to use sendevent, however as I figured since posting, sendevent will not work on later Android devices, as access to input devices from shell has been explicitely disabled in SELinux policy. I don't know a way to work around this without rooting. The recommended way is to use UiAutomation#injectInputEvent. That's Java code, so one could write a service, which forwards events received on some interface. That leads far way beyond this question. Anyway, my sendevent solution you may still use on some devices.


You can find complete sendevent examples online (eg in this blog post), but none worked for me out of the box, so I had to tweak things a bit.

First of all, if you want to use sendevent, you should figure out the right event device first. getevent can be used to obtain this info, either getevent -p or getevent -i will tell you the event dev <-> device connections, so you can find the event associated with your touchscreen device.

You can also use getevent to see how a real tap event looks like on your device. Just run adb shell, start getevent, then tap on the screen (for some reason adb shell getevent didn't return the events):

/dev/input/event2: 0003 0039 000025e9
/dev/input/event2: 0001 014a 00000001
/dev/input/event2: 0003 0035 00000320
/dev/input/event2: 0003 0036 000002ae
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0030 00000005
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0039 ffffffff
/dev/input/event2: 0001 014a 00000000
/dev/input/event2: 0000 0000 00000000

You get a bunch of hex codes. Try the same with getevent -l:

/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   000025e8
/dev/input/event2: EV_KEY       BTN_TOUCH            DOWN
/dev/input/event2: EV_ABS       ABS_MT_POSITION_X    000002f8
/dev/input/event2: EV_ABS       ABS_MT_POSITION_Y    000002f2
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MAJOR   00000005
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MINOR   00000004
/dev/input/event2: EV_SYN       SYN_REPORT           00000000
/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   ffffffff
/dev/input/event2: EV_KEY       BTN_TOUCH            UP
/dev/input/event2: EV_SYN       SYN_REPORT           00000000

and you will see the labels for the codes (where applicable), so you can match the codes with the labels. If you don't want a generic solution, just want it to work on your own device, then you only have to recreate the tap sequence you've just seen. (Be careful with the hex codes, getevent prints hex, but sendevent expects decimal.)

This is the code I use in one of my scripts:

EV_SYN=0
EV_KEY=1
EV_ABS=3
BTN_TOUCH=330
BTN_TOUCH_DOWN=1
BTN_TOUCH_UP=0
SYN_REPORT=0

ABS_MT_TRACKING_ID=57
ABS_MT_POSITION_X=53
ABS_MT_POSITION_Y=54
ABS_MT_TOUCH_MAJOR=48
ABS_MT_TOUCH_MINOR=49

event_dev=`adb shell getevent -pl 2>/dev/null | sed -e ':a;N;$!ba;s/\n / /g' | awk '/ABS_MT_TOUCH/{print \$4}'`

tracking_id=1111
POS_X=600
POS_Y=800

adb shell "
        sendevent $event_dev $EV_ABS $ABS_MT_TRACKING_ID   $tracking_id
        sendevent $event_dev $EV_KEY $BTN_TOUCH            $BTN_TOUCH_DOWN
        sendevent $event_dev $EV_ABS $ABS_MT_POSITION_X    $POS_X
        sendevent $event_dev $EV_ABS $ABS_MT_POSITION_Y    $POS_Y
        sendevent $event_dev $EV_ABS $ABS_MT_TOUCH_MAJOR   5
        sendevent $event_dev $EV_ABS $ABS_MT_TOUCH_MINOR   4
        sendevent $event_dev $EV_SYN $SYN_REPORT           0
"

adb shell "
        sendevent $event_dev $EV_ABS $ABS_MT_TRACKING_ID   -1
        sendevent $event_dev $EV_KEY $BTN_TOUCH            $BTN_TOUCH_UP
        sendevent $event_dev $EV_SYN $SYN_REPORT           0
"

It works on my non-rooted device, but you may have to tweak it to match your device codes and sequences.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .