Our smartphones are capable of some impressive processing power, quickly replacing much of what we used the desktop PC for, in the past: mainly document management and communication. But there's always a balance between functionality and security, and Google's Android decided to opt mostly in favor of the latter, leaving out critical functionality on purpose, such as injecting keys or touch events to a third party process.
While this seems unimportant to the regular user, this is fundamentally crucial for developing "also regular" applications such as: remote administration (like VNC, teamviewer, remote control), for application test tools, or for applications aiming to improve the smartphone control for persons with disabilities (using the chair joystick to control the Android phone). Another example here, is the option to create a custom mouse pointer on top of your Android screen, for application control purposes, as presented here, that also needs to be able to inject the touch events when the user clicks.
Not to mention various automation tasks, hacks & mods but in the end is all about the freedom given to the development community. The Android platform makers should come up with some better solutions.


In part 1 of this article I have indicated three ways of injecting events on the Android platform:
Method 1: Using internal APIs, mainly relying on the WindowManager and the two APIs: injectPointerEvent and injectKeyEvent

Method 2: Using an instrumentation object

Method 3: Direct event injection to /dev/input/eventX

While the first two rely on the dreaded INJECT_EVENTS permission, available only to platform makers, and have suffered great changes in the last OS versions, the third method is at the moment of writing this article the best bet for properly injecting events on Android.

The /dev/input/eventX are event input nodes, part of the linux platform beneath Android. Normally their permissions are set so only the Owner and its Group can read and write from/top these files. There are no permissions set for Other, and there is where our code is situated, since it is not owner of the linux input event nodes, nor member of the Owner group:

So to be able to use method 3, and to directly interface to the input event nodes, we need to be able to alter the permissions, in order to set the Other permission bit to Read (if we want to intercept input event data) or Write (if we want to push events, as indicated in the article's title: Injecting events). The best way to go is to set the bit to allow +rw (read and write) , using the chmod native command:
Without root access, this fails, with "operation not permitted". So here is the point where we can only proceed if we are using a Rooted Android phone. Everything else shown in this article will work without root, but for changing the permissions we need root, and there's no way around it. A bit frustrating, considering we are talking about such a tiny information segment. More on linux filesystem permissions, here.

Once the input event nodes are set to allow both reading and writing, we can open them as regular files, and read the content or write our own content, to intercept events or inject our own keys or touch events. The format accepted by the input event node files is:

  1. struct input_event {
  2. struct timeval time;
  3. unsigned short type;
  4. unsigned short code;
  5. unsigned int value;
  6. };
  7. /* type and code are values defined in linux/input.h.
  8.  * For example, type might be EV_REL for relative moment of a mouse,
  9.  * or EV_KEY for a keypress, and code is the keycode, or REL_X or ABS_X for a mouse.
  10.  * The linux/input.h is part of the Android NDK
  11.  */

Make sure to read more on this topic, for a better understanding. A nice article is available on Linux Journal.

By popular demand on my two previous articles: Programmatically Injecting Events on Android – Part 1 and Android Overlay Mouse Cursor, but also to standardize method #3, I have developed a JNI library for Android, to take care of all these details to do what the standard API refuses to do: simple event injection for all your development needs. But it requires root, as explained previously.

The library, named "android-event-injector" is released as Open Source, under GPL, and available on Google code, here.

Android Event Injector functionality

android_inject_touch_key_event_diagram This library uses JNI, and you will need to setup Android NDK if you want to change it or compile it.

It uses a low level, native component, that completely handles the input event node interaction: discovering the files (ScanFiles), opening them (OpenDev), Closing/Removing allocated memory (RemoveDev) or getting event node details such as Path and Name. With an open Input Event Node, we can do polling (PollDev, getType, getCode, getValue) or event injection (intSendEvent).

Via JNI, we are handling all this functionality in Java (Events.Java). This class builds multiple instances of the InputDevice class, that correspond to the number of discovered native input event nodes (the number of /dev/input/eventX files). When we open such a file, we use the API call Open(boolean forceOpen):

  2. /**
  3. * function Open : opens an input event node
  4. * @param forceOpen will try to set permissions and then reopen if first open attempt fails
  5. * @return true if input event node has been opened
  6. */
  7. public boolean Open(boolean forceOpen) {
  8. int res = OpenDev(m_nId);
  9. // if opening fails, we might not have the correct permissions, try changing 660 to 666
  10. if (res != 0) {
  11. // possible only if we have root
  12. if(forceOpen && Shell.isSuAvailable()) {
  13. // set new permissions
  14. Shell.runCommand("chmod 666 "+ m_szPath);
  15. // reopen
  16. res = OpenDev(m_nId);
  17. }
  18. }
  19. m_szName = getDevName(m_nId);
  20. m_bOpen = (res == 0);
  21. // debug
  22. Log.d(LT, "Open:"+m_szPath+" Name:"+m_szName+" Result:"+m_bOpen);
  23. // done, return
  24. return m_bOpen;
  25. }

As you can see, at this point we also use chmod to make sure our process can read and write the input event node (/dev/input/eventX file).

If we are successful in opening an event node, we can use it for listening for events (system touch events, key presses, etc), or for injecting events. The code on Google code, exemplifies all these:

Listening for incoming events

Here is a simple example on how to use my library, to create a thread that intercepts all system input events and shows them in logcat:

  2. /**
  3. * Starts our event monitor thread that does the data extraction via polling
  4. * all data is displayed in the textview, as type-code-value, see input.h in the Android NDK for more details
  5. * Monitor output is also sent to Logcat, so make sure you used that as well
  6. */
  7. public void StartEventMonitor() {
  8. m_bMonitorOn = true;
  9. Thread b = new Thread(new Runnable() {
  10. public void run() {
  11. while (m_bMonitorOn) {
  12. for (InputDevice idev:events.m_Devs) {
  13. // Open more devices to see their messages
  14. if (idev.getOpen() && (0 == idev.getPollingEvent())) {
  15. final String line = idev.getName()+
  16. ":" + idev.getSuccessfulPollingType()+
  17. " " + idev.getSuccessfulPollingCode() +
  18. " " + idev.getSuccessfulPollingValue();
  19. Log.d(LT, "Event:"+line);
  20. // update textview to show data
  21. //if (idev.getSuccessfulPollingValue() != 0)
  22. m_tvMonitor.post(new Runnable() {
  23. public void run() {
  24. m_tvMonitor.setText(line);
  25. }
  26. });
  27. }
  28. }
  29. }
  30. }
  31. });
  32. b.start();
  33. }

This is very useful when you need to better understand the event structure and the involved values for type,code and value. Make sure you have a look on linux/input.h contents as well, inside your Android NDK folder.

Injecting events

Again, input.h is our friend, since it details the event codes we need to use. But you will also want to use the event monitor to be able to write down all the complex events sequence generated by the system when the user touches the screen. Here are a few wrapper functions to serve as a sample:
Home hardware key injection:

  2. /**
  3. * Finds an open device that has a name containing keypad. This probably is the event node associated with the keypad
  4. * Its purpose is to handle all hardware Android buttons such as Back, Home, Volume, etc
  5. * Key codes are defined in input.h (see NDK) , or use the Event Monitor to see keypad messages
  6. * This function sends the HOME key
  7. */
  8. public void SendHomeKeyToKeypad() {
  9. boolean found = false;
  10. for (InputDevice idev:events.m_Devs) {
  11. //* Finds an open device that has a name containing keypad. This probably is the keypad associated event node
  12. if (idev.getOpen() && idev.getName().contains("keypad")) {
  13. idev.SendKey(102, true); // home key down
  14. idev.SendKey(102, false); // home key up
  15. found = true; break;
  16. }
  17. }
  18. if (found == false)
  19. Toast.makeText(this, "Keypad not found.", Toast.LENGTH_SHORT).show();
  20. }

And another example, for injecting touch events:

  1. //absolute coordinates, on my device they go up to 570x960
  2. if (m_selectedDev!=-1)
  3. events.m_Devs.get(m_selectedDev).SendTouchDownAbs(155,183);

Don't forget to correctly use the Event input devices. When you open them, you also have access to the Device Name. Make sure you insert key events to the device named "keypad" or "keyboard" and not to the compass or other input devices if you want this to work. Also for touch events, make sure you insert your touches/click events to "touchpad" or "ts". The namings can be different from a device to another, or from a platform to another. Try to develop an algorithm to correctly identify your target before injecting input data.

android_inject_keys_touches_4 android_inject_keys_touches_3 android_inject_keys_touches_2 android_inject_keys_touches_1

Additional resources:

Programmatically Injecting Events on Android – Part 1
Android Overlay Mouse Cursor
The code is available on Github, under GPL. Use it only if you understand the terms of Open Source software distributed under GPL.
The library and a sample can be downloaded here or EventInjector

Related Post