6 Jul 2012

Forwarding evdev devices to Android

I'm working on adding input support for Weston's Android backend, and to test a normal keyboard and a mouse, I needed a way to get those as evdev devices on Android. I don't have any Bluetooth devices here, so I started hacking up evdev forwarding from a laptop PC. I could not find much information on anyone doing this before.

First I tried the various forwarding types of adb other than TCP. None of them seemed to work at all. Therefore I needed to use TCP, since that worked. Thanks to a co-worker at Collabora, I found out that busybox has a netcat tool I could use on Android. Laptop of course has the original netcat. Nethid is a quick & dirty program for creating a fake evdev device node using uinput, so I fixed that to work on Android.

First on the host, set up TCP forwarding (port number just made up):
$ adb forward tcp:7776 tcp:7776
On the Android device, start uinput:
$ busybox nc -l -p 7776 | uinput-pipe
Then on the host start feeding input events:
$ cat /dev/input/event10 | nc localhost 7776
And Weston now finds my forwarded evdev input device and receives input from it!

But there are some minor problems with this. Something in this pipe spaghetti is caching data, so input events arrive in bursts, not as soon as possible. Also, something is eating over 99% of the REL_X events, so it is quite impossible to move the cursor horizontally. REL_Y events seem go through fine. Strange...

Anyway, this fills its purpose: I was able to test that pointer devices work on my Weston android backend, and I also got mouse cursors to show up after some fixes. The video below is the proof.


mikolatorbins said...

Hi Pekka!
And what about different wigetsets? Will they need adaptation for Android? As I read, Qt already has android backend, but Gtk hasn't. Does this mean that Gtk-apps will have to use software rendering? Or maybe they will use some wayland protocol for hardware acceleration?

pq said...

mikolatorbins, Android is a different operating system, and usually porting is not about simply recompiling. The build system and the deficiencies in the C library are the first obstacles you hit. So yes, adaptation for Android is needed, but not for graphics. If a toolkit supports Wayland, it supports Wayland on Android, too. Hardware graphics acceleration included, once we get the enablement done, which is still several months of work. Wayland protocol offers no rendering, at all. Accelerated rendering is done via EGL.

Mihara said...

Hi. Sorry for the tangential question, but looks like I'm out of other avenues of research and need to ask someone who's been messing with the input subsystem already. :)

I'm looking for a means to temporarily hide the mouse pointer. My particular Android device has no touch input, and uses a HID mouse with a few keys on it. I wish to use it to show a presentation, but since I can't hide the mouse cursor, it would constantly move around on the screen because the stock input device is accelerometer based. Apparently, there's no ready-made solution anywhere. One of the avenues I've been investigating is somehow intercepting the /dev/input and removing mouse positioning events from it, or alternatively, constantly injecting events which force the mouse pointer to stick to the corner where it's not visible, or something to that effect -- If I could program it to listen to a keypress event from the same input device to toggle that behaviour on and off, that would be just perfect.

Could you possibly enlighten me on whether it's feasible and provide any useful tips or pointers to literature?

pq said...

Mihara, faking new events like that sounds like a wrong approach. I have no idea how to hide a mouse cursor in Android middleware or apps. But, would you like to disable that one input device completely? When I read the Android input middlelayer, I saw reference to a file, that could be used to ignore evdev devices. On GN it didn't exist, and I can't remember what it was, but maybe that could be a lead?

Mihara said...

If I completely disable the input device, I will be unable to control my presentation, since that wireless mouse is the only input device this android machine has. (For the record, it's a variant of a CX-01 Android Mini PC, a HDMI dongle with two USB ports and nothing else on it. There are numerous variations, but most come bundled with one or another little-known wireless mouse/keyboard combo brand. Very cost-effective for what it can do, if I can just solve that silly problem.) However, the wireless mouse (when plugged into a bigger computer, but I imagine Android gets the same results) presents itself as a single USB composite device which produces three separate HID streams (mouse, keyboard, and 'device' - no idea what the third one does or whether it sends anything.).

I suppose if I could temporarily shut off only the mouse part, that would cause the framework to remove the cursor after a period of inactivity, which would do fine - if I can get it back later without forcing a system reboot. (If you just pull out the mouse's USB dongle, the system reboots. No idea why.)

Could you possibly remember something I could use as a google search string? Naive googling doesn't seem to turn up anything relevant.

Mihara said...

Downloading the entire AOSP repo and grepping through the source did turn up leads, but didn't help, at least directly. There is apparently some method to add a device into an 'exclude list'. However, from what I can see in the sources, that would only affect an input device while it's being plugged in, so it's probably unsuitable for my problem.

Nevertheless, finding the sources for 'toolbox' in the AOSP repo did lead me to a simple and elegantly brute solution that seems like it's going to work with just a little more effort, and might even be suitable for public consumption someday -- I'm probably not the only person around with that kind of problem.

For the record, here's what I did:

1. I took the code from http://stackoverflow.com/questions/7668872/need-to-intercept-hid-keyboard-events-and-then-block-them -- The snippet listed therein shows how to request exclusive access to /dev/input/event2 (always the mouse in my case) and ignore the events coming from it, which disables the mouse, but leaves the keys running. As I expected, the mouse pointer vanishes after a certain inactivity timeout as the android framework forgets about it.
2. I used the recommendations from http://stackoverflow.com/questions/9324772/cross-compiling-static-c-hello-world-for-android-using-arm-linux-gnueabi-gcc to produce a static binary that I placed into /system/xbin.

While the said binary is running, mouse events are ignored and mouse pointer is not shown, so it's only a matter of tweaking it to make it enable/disable itself on a keypress. The remote includes a conveniently non-working mute button which does send an input event, (just not the one Android expects) so I should be able to latch onto that, and then use a shell script manager app to start/stop the binary.

Thank you for your tip, it's been most helpful.

pq said...

Mihara, hey, nice work! You should probably blog about that. ;-)

Mihara said...

I will, when I can clear up other pressing business. :)

Haier Hei said...

Good! I'm using rinput => https://github.com/heiher/rinput

Post a Comment