Programmatically Injecting Events on Android – Part 2

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.
header_android_inject_events

Introduction

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:
android_input_event_nodes

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:
android_input_event_nodes_chmod
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:

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

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):

/**
* function Open : opens an input event node
* @param forceOpen will try to set permissions and then reopen if first open attempt fails
* @return true if input event node has been opened
*/
public boolean Open(boolean forceOpen) {
  int res = OpenDev(m_nId);
  // if opening fails, we might not have the correct permissions, try changing 660 to 666
  if (res != 0) {
    // possible only if we have root
    if(forceOpen && Shell.isSuAvailable()) { 
      // set new permissions
         Shell.runCommand("chmod 666 "+ m_szPath);
         // reopen
           res = OpenDev(m_nId);
       }
  }
  m_szName = getDevName(m_nId);
  m_bOpen = (res == 0);
  // debug
  Log.d(LT,  "Open:"+m_szPath+" Name:"+m_szName+" Result:"+m_bOpen);
  // done, return
  return m_bOpen;
}

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:

/**
* Starts our event monitor thread that does the data extraction via polling
* all data is displayed in the textview, as type-code-value, see input.h in the Android NDK for more details
* Monitor output is also sent to Logcat, so make sure you used that as well
*/
public void StartEventMonitor() {
m_bMonitorOn = true;
Thread b = new Thread(new Runnable() {
  public void run() {
    while (m_bMonitorOn) {
      for (InputDevice idev:events.m_Devs) {
        // Open more devices to see their messages
        if (idev.getOpen() && (0 == idev.getPollingEvent())) {
          final String line = idev.getName()+
              ":" + idev.getSuccessfulPollingType()+
              " " + idev.getSuccessfulPollingCode() + 
              " " + idev.getSuccessfulPollingValue();
          Log.d(LT, "Event:"+line);
          // update textview to show data
          //if (idev.getSuccessfulPollingValue() != 0)
          m_tvMonitor.post(new Runnable() {
            public void run() {
              m_tvMonitor.setText(line);
            }
          });
        }
      }
    }
  }
});
b.start();    
}  

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:

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

And another example, for injecting touch events:

//absolute coordinates, on my device they go up to 570x960
if (m_selectedDev!=-1)
   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

This article has 87 Comments

  1. Hi Radu. Thanks for all the information you put together on injecting events into Android.

    I’ve been working on a project to do this as well. It’s a peer to peer app where one Android device can control another one. For the most part, it is working. I can send mouse movement and keyboard events using the instrumentation method, and draw a custom cursor around the screen. And I can inject events into other apps OK. I haven’t jumped over to the EventX method because I didn’t want to deal with the issues in picking the right input nodes and changing permissions. But it may get to the point where I have to do that.

    Where I am stuck now is that I can’t figure out what the right action is to simulate an actual tap through instrumentation – i.e. pressing a button. I’ve tried the BUTTON_PRIMARY action, as well as sending an ACTION_DOWN followed by ACTION_UP and no change in x and y, but that didn’t work. Any ideas? Also, I can’t seem to get other actions like home and back working.

    Thanks!
    -Gregg Reno
    gregg@greggreno.com

  2. Hi Gregg. Glad you found it useful. You can use instrumentation, only inside you app, or if your application has the INJECT_EVENTS permission and is signed with the platform signature, then you can also inject in other processes. I assume this is how you’ve used instrumentation.

    Eventually you’ll need to rely on the input event nodes and the technique presented here, at the cost of having your app run on rooted devices only. Choosing the right EventX might seem tricky, but this is exactly why I wrote this lib in the first place: you now have access to a list of strings representing the input device and you can choose. It is also possible to add more functionality to the JNI library, in order to gather more info on the input node properties, I might do that later, also based on the interest users show. If you are only using the name as a reference, then you’ll need to sort the input event nodes based on common strings like “touchpad” for touch events or “keypad” for key injection. Quite easy.

    Back to your question, I’d like to know more on your setup: OS version, and whether you’re injecting inside your own process app, or trying to reach third party processes. As I explained above, special permissions are required outside your own app.

    Also make sure you see the sample code I posted here: http://www.pocketmagic.net/2012/04/injecting-events-programatically-on-android/

    Good luck!

  3. Hi Radu. You have done a great job, at least for these people like me who are trying to “break” the way google has developed handle of events. Actually, I´m writing my final project at the university and to be honest your article, your library, and your samples are just what i was looking for! Excelent job!

    I´ve found your article some days ago and since then I am working on it no problems so far. By the way I am going to write what im working on, maybe you will have some easier approach than mine.

    My app has to take some touch events (the events which will not be handle by my app should be send to the activity running under my overlay). From what I understood in your article I´m trying to make an approach, getting all touch events, making some calculations, decide if the my app has to handle this particular event, in affirmative case do whatever and in negative case send it to the app running under mine (the user only sees the second app, not mine)

    Thanks!

  4. Hey Radu, I just realised that in the sample published in the second part of the article I cant see the bottom of the app screen, where is showed the monitor.

    I have two devices and I just checked in both and I have the same.

    Just telling because you may want to check it.

  5. Thanks, I am aware of that – if one has too many input event nodes, the interface will not scroll, but instead will push the bottom content out of the screen. I’ll fix it when I get the chance.

  6. Hello again Radu.

    Hope I am not asking too much. Just a simple question, I have done some aproaches but I dont get with the key.

    is it possible somehow to read from /dev/imput/eventX using any of these functions getPollingEvent(), getSuccessfulPollingType(), getSuccessfulPollingCode() ,getSuccessfulPollingValue(); deleting the entries from the file?

    or in a easier way

    I need to read from eventX ( X = touchpad ) when any other app is running “in the screen”:

    1. In case I dont have to handle this touch event, will it just go to the app running? or I have to inject it once again?
    2. In case I have to handle this event, I need it to not be sent to the app running.

    So, is the information deleted from /dev/imput/eventX when you read it? In negative case, there is some how to do it?

    I really appreciate your help.

    Thanks in advance

  7. Hello again Radu,
    I´ve been looking for further information, but still I cant achieve what I need.

    I have new approach but I need to know something to be sure if it works, so the question is: When I inject event in the system using your method, you do it to the eventX, so the system will read the event from this file and do what it has to do to send if to the app which runs the activity actually showed in the screen? am I right?

    Thanks a lot.

  8. Hey Mr.Radu, I am a final year student currently study at Liverpool University. I am doing a porject that gain a lot of help form you founds. I am surprise you also provide the way to overlay mouse cursor on the phone, which also what I need for my resaerch project. I can say with out your work I may never achive this far, thank you very much.

  9. Hi, I managed to get this to compile, despite not having any previous experience with android ndk. One question though, I can inject touch events to my own application, but how can I specify another one? For example, if I have a drawing application open, and then open my own application, such as a single button on a transparent background, and by pressing this button, it should interact with the canvas below. Would this be possible? or have I missed the whole point of this code?

    Thanks, Simon.

  10. Hey Mr.Radu. I want to ask if you know how to inject the multi-touch events? It seems the ways your provided only work for the single touch event.

  11. @Simon, you can have the injecting code run in the background, so the clicks will go to any application currently on the screen.

    @Shawn, you can use the event monitor provided in my sample, to see what messages are sent for the multitouch events. Then you can simulate them using the inject code.

  12. Hello Radu, finally I succeeded handling events and understanding how it works.

    Now I am trying to create my own project to use your library. I am having some problems because when I try to run the project I get error:

    “java.lang.UnsatisfiedLinkError: Native method not found:”

    I was also checking that in the file EventInjector.c the head of the methods have for example:

    “jint Java_net_pocketmagic_android_eventinjector_Events_intEnableDebug( JNIEnv* env,jobject thiz, jint enable ) {”

    So, it means that I have to recompile using NDK?

    I am sorry if it is a stupid question but I have never been working on that point.

    Thanks in advance.

  13. Hi Carlos,

    It would be better you study a bit more on the way NDK works because it would help you understand this kind of errors.

    Nevertheless I’ll assist you with this problem:
    – there must be an exact correspondence between the function naming in the JNI cpp files, and the naming used in the Java files or you’ll get the “native method not found”.

    As you can see the native functions are named: net_pocketmagic_android_eveninjector_Events_functionname

    So what you need to do, is make sure your src folder contains:
    \src\net\pocketmagic\android\eventinjector\Events.java

    Do not change this!

    The correct approach is to simply import the \src\net\pocketmagic\android\eventinjector\Events.java to your project, keeping the directory structure as presented here.

    You can have your other project files in separate folders:

    ex: \src\com\carlosweb\android\carlosproject\MainActivity.java etc

  14. Hi Radu,

    Thanks for this great article. I so envy you big time. 🙂

    I would just want to ask what is the corresponding click event for the EventInjector. I tried SendTouchDownAbs(170,270) but I failed to make it click. I am sure the x – 170 and the y – 270 points to a icon on the screen that when tapped should open its app.

    I hope you could help me on this.

    Thanks.

  15. Hi MArk,

    Glad you find this useful. The coordinate metrics are a bit strange. Best thing to do is to use the Event Monitor, and see the exact coordinates you receive for your screen.

    Maybe you have the time to write a translation function and you want to share that with the community. For me I had to let this project as it is , due to other projects.

    Bests,
    Radu

  16. Thanks Radu,it works great now.
    It took a lot of thoughts but finally I think I understand how your library works. It´s just great and my best friend now 😉

    Also answering to Mark dev question: As Radu wrote in his post, the best approach is first to monitorize the events and just create your own function with your proper sequence of intSendEvent();

    In my case I found that the event sequence for the click event is the following:

    public int SendTouchDownAbsMIO( int x, int y){
    intSendEvent(m_nId, 3, 53, x);
    intSendEvent(m_nId, 3, 54, y);
    intSendEvent(m_nId, 3, 58, 64);
    intSendEvent(m_nId, 3, 48, 4);
    intSendEvent(m_nId, 3, 57, 0);
    intSendEvent(m_nId, 0, 2, 0);
    intSendEvent(m_nId, 0, 0, 0);
    intSendEvent(m_nId, 0, 2, 0);
    intSendEvent(m_nId, 0, 0, 0);
    return 1;
    }

  17. Hi,

    i try to use Android event injector to get touching screen events but i dont know where it is stored after stoping monitor? Is it any file on the phone in specified directory?

  18. Hi once again,
    my name is Maciej. I am contacting with you throught your blog becouse I do not fund your email.
    I am trying to write script which will click the Android phone screen on the specified place In every few secounds in a loop. I am confused becouse I Am fighting with this from 2 weeks and no result. I am not programer but in the past I write some small scripts in pearl and php.
    Is there any possibility that you help me to write that script?
    Please do not hestitate to contact with me.
    regards
    Maciej

  19. @Carlos: thanks for your contribution, lets hope it will help others too.

    @maciej: I sent you an email.

  20. Hi Radu,

    I’ve tried to use you two sample codes and the .apk file. All of them run stabie but everytime whene I chick to injekt some touchevents my display freezes and nothing happens.
    I Have to restart the phone by hard resetting it.
    I’m usung Samsung Galaxy S3 with Android 4.1.2

    Greetings Matthias

  21. Hi Radu,
    Thanks to your sample i successfully adapted to implement drag/scroll.
    Using Nexus One with Android 4.2.2

  22. @szihs: sounds good! some of the users in the comments above, mentioned issues in using the code on android 4.2 . Can you help them with some hints?

  23. #adb shell getevent -l /dev/input/event3
    Do any event on touchscreen.
    EV_ABS ABS_X 000007a8
    EV_ABS ABS_Y 00001346
    EV_ABS ABS_PRESSURE 00000057
    EV_KEY BTN_TOUCH DOWN
    ==
    The type-code-value will be output on terminal. I didn’t face screen freeze but i found #getevent extremely informative.

    I found this more easy to read and understand the sequence.

    2.
    adb shell sendevent device type1 code1 value1
    adb shell sendevent device type2 code2 value2
    to see you sequence works

    Replay it using code.
    intSendEvent(m_nId, EV_ABS, ABS_X, x);
    intSendEvent(m_nId, EV_ABS, ABS_Y, y);
    intSendEvent(m_nId, EV_ABS, ABS_PRESSURE, p);
    intSendEvent(m_nId, EV_KEY, BTN_TOUCH, 1);

  24. I encountered the touchscreen freeze problem too on my Nexus 7 with Android 4.2.2.
    After investigating, I figured out how to fix the problem on my side (thanks to the suggestion from @szlhs )
    The root cause is my touchscreen device sends different event data sequences from what the sample project sends.
    I suggest anyone who encounter this problem first use the “monitor” feature (by pressing “montor start”) and watch the event sequences in the log console.
    Here is my captured event sequence for tap touch event sequences:
    ===
    05-20 09:12:05.600 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 57 61
    05-20 09:12:05.610 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 48 6
    05-20 09:12:05.610 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 18
    05-20 09:12:05.610 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 848
    05-20 09:12:05.610 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1690
    05-20 09:12:05.610 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:12:05.660 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 57 -1
    05-20 09:12:05.660 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    ===

    And here is the touch -> drag -> release event sequences:
    ===
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 57 64
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 48 5
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 16
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 494
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1235
    05-20 09:14:39.240 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:14:39.460 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 101
    05-20 09:14:39.460 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 489
    05-20 09:14:39.460 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1236
    05-20 09:14:39.470 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:14:41.800 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 110
    05-20 09:14:41.800 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 488
    05-20 09:14:41.800 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1231
    05-20 09:14:41.800 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:14:41.890 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 111
    05-20 09:14:41.890 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 493
    05-20 09:14:41.890 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1229
    05-20 09:14:41.890 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:14:42.180 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 58 114
    05-20 09:14:42.180 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 53 498
    05-20 09:14:42.180 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 54 1228
    05-20 09:14:42.180 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:0 0 0
    05-20 09:14:43.380 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan-touchscreen:3 57 -1
    05-20 09:14:43.380 1597-1619/net.pocketmagic.android.eventinjector D/MainActivity: Event:elan
    ===

    According to the captured pattern I managed to make the sending touch feature work again by modifying the event sequence to send.
    I am wondering if the event sequences to control are different according to different type of touchscreen device…

  25. Is there a way to force android to ignore events?
    If i have an app that fetches e.g. gamepad input … can i prevent the systems default action (e.g. Back-Button). Even if my app is not focused.
    Can anyone help me? I’m going crazy with this 🙁

  26. @wangpy: yes, this is why I offered the monitor option

    @sascha: I don’t think this is allowed on android. you know… security paranoia

  27. @Radu: thanks for the answer and this awesome project. i think you’re right 🙁 i wanted to develop an app that tracks gamepad input to touch input. everything works fine with the buttons that are’nt mapped by system, but i cannot use the gamepad full, cause some buttons are mapped to “back” 🙁 That’s so bad 🙁

  28. Sorry to hear that, I know how it feels, I’ve been hitting various Android limitations myself, but nothing to do – nobody cares.

  29. Hi and thank you for this library.
    I am having a problem with a virtual device that I create using uinput:
    the device is listed and my function returns success value, so it should be created and working.

    By the way, if I run a touch or key test nothing happens, even if my device is there and I can open and select it.

  30. Hi,

    I have checked logs for something but everything seems fine.
    The logs say that keyevents have been sent to the correct device succesfully, but nothing happens.

  31. Hi again, your lib doesn’t do EV_SYN SYN_REPORT after an event is sent, it only works with devices that have sync in their code, but it is a bit difficult to include it on virtual device, so I added a function to do EV_SYN on my device.

  32. @Sascha: Assuming that your app will be device-dependent you can create a Set that works like a “whitelist”, so if the key pressed is in it, it’s processed, otherwise it won’t.
    I am trying to develop an application like yours, but I’m stuck at the point that I don’t know how to “block” the real keyevent so that only the injected event is detected by android.

  33. Which one would be the correct way to find the id of a device given its name?
    The following code is very slow and buggy:

    public int findDevice(String name, boolean su) {
    if (null != m_Devs) {
    for (int i = 0; i < m_Devs.size(); i++) {
    if (m_Devs.get(i).Open(su)) {
    Log.i("FindDevice", i + ")" + m_Devs.get(i).getName());
    if (m_Devs.get(i).getName().equals(name)) return i;
    }
    m_Devs.get(i).Close();
    }
    }
    return -1;
    }

  34. Hello @Radu,
    First, thanks a lot for your Library.
    I am testing your Library, the touch events are working very good, but 🙁 the key events not work, I revised the log and I see all fine, I am injecting key events but nothing happen, the permissions are configured correctly, Exists any trick?
    Thanks again !!

  35. @Aldebaran: edit the sendkey method this way:

    public int SendKey(int key, boolean state) {
    if (state)
    intSendEvent(m_nId, EV_KEY, key, 1); //key down
    else
    intSendEvent(m_nId, EV_KEY, key, 0); //key up
    return intSendEvent(m_nId, 0 , 0 , 0);

    }

  36. Hey Radu, your articles are really helpful. But I’m having a problem that whenever I declare “Events events = new Events();” in my activity my app is FC’ing at startup. Please help me out. Thanks in advance mate.

  37. Hi Radu,

    I have problem with method 3. In file input.c command ioctl(fd_kb, UI_SET_EVBIT, EV_KEY) return fail.

    Please help me.

    Thank you so much.

  38. I found a way to block events from a device:

    jint Java_com_myapp_example_util_InputDeviceManager_takeOverDevice(JNIEnv* env, jobject thiz, jint index, jint control){
    int fd = pDevs[index].ufds.fd;
    if(fd=nDevsCount) return -4;
    int result = ioctl(fd, EVIOCGRAB, control); //1=grab | 0=release
    return result;
    }

    If you’re developing a IME you can start a thread that keeps reading events from the device (like the monitor of sample app does) but even if you can read events they won’t be spread to the java level. then create a virtual device with uinput and forward events or create new ones and send them to that device, it will become your main device when ime is on.

  39. Hi Alessandro, it definitely is a great find…But for me whenever I use it my inputmethodservice is crashing..Please help me with some example code about how to use it exactly….Thanks in advance..

  40. Ok Alessandro…Thanks for the code…Have solved my problem…You’ve missed an “=” in the if statement which made the app to crash…Otherwise it works like a charm…

  41. @Ankit: Actually that was a greater than or equal, not just equal, but the comment filter of the blog censors the major/minor characters, maybe to prevent usage of html code 🙂

  42. Ok, So I’m trying to install NDK and compile EventInjector and it’s giving me this error and I have searched to solve the error but am unable to figure it out for the past 2 days – On running ndk-build – In file included from jni/EventInjector.c:55:0:
    jni/EventInjector.h:43:9: error: ‘INPUT_PROP_POINTER’ undeclared here (not in a function)
    jni/EventInjector.h:44:9: error: ‘INPUT_PROP_DIRECT’ undeclared here (not in a function)
    ….(another 100 lines similar to this)
    error: ‘MT_TOOL_MAX’ undeclared here (not in a function)
    jni/EventInjector.h:720:9: error: initializer element is not constant
    jni/EventInjector.h:720:9: error: (near initialization for ‘mt_tool_labels[2].value’)
    /cygdrive/c/Current/setups/android-ndk-r8e-windows-x86_64/android-ndk-r8e/build/core/build-binary.mk:269: recipe for target `obj/local/armeabi/objs/EventInjector/EventInjector.o’ failed
    make: *** [obj/local/armeabi/objs/EventInjector/EventInjector.o] Error 1

    Could you give me any clues how to solve this?

  43. I have been trying to solve the above error and it seems its caused due to ndk not finding linux/input.h anywhere. Can we solve this somehow? Also, simply downloading the EventInjector zip and importing it into eclipse and running it also gives an error – not sure if we are actually even supposed to run ndk-build if we only need to compile. I’m using Nexus 4 and there is no armeabi-v7a folder in the obj and libs folder as some websites have pointed out. However directly installing the apk in phone works. Totally lost here, could someone please help with this?

  44. @Aakar: I had the same issue and imported my own input.h version containing all the missing declarations.

  45. Thanks for this very helpful article.
    I do have a problem with your suggestion of changing the permissions on the /dev/event* device files: any other app (whether granted root or not) has full access to these files (well, until the next reboot).

    Why not run a small service binary (owned by root, chmod 500) to access /dev/input* and communicate with the app which want to inject events?
    Have the app (which the user needs to grant root access) start and stop this service. This should reduces the risk of some random app hijacking your touchscreen to plunder your bank account, etc.

  46. Hi Alain,

    Thanks for your suggestion; the purpose of this article was demonstrating the input event functionality and not the security issues. Would love to handle everything very much like you have described but time is not my friend.

    Others can build upon my work and take care of the remaining issues, the source code is open source.

  47. Thanks for the great article.

    I’m facing the same problem as Aakar and i can’t get it to work. It is the first time i am working with ndk so if you could give me a hand on what to do?

    I tried importing the input.h as suggested but i keep having a lot erros due to undeclared values…

    Should i just declare the variables myself?
    Sorry if it is a simple question and thanks in advanced.

  48. Apparently posting the question was all i need to resolve most of my issues.
    Had one version of the input.h which did not contained all i needed.

    Thank you anyway.

  49. I implemented the project, changed the sendkey function Alessandro told us (41) but im still not able to inject key events. Im using a galaxy nexus with androd 4.3. Any suggestions?

    Thanks already

  50. Hello @Motisan, thank you very much for your great post which helps me much.But I have one question, how can we detect which device in /dev/input/eventX represents the touchpad and which represents the keypad?I’ve tried catting /proc/bus/input/handlers to find out something, but it doesn’t work.It still can’t detect which means what from the name properties.Is there any exact method?Thank you for your help!

  51. Hello again,

    I’m trying to do what @Alessandro suggested “create a virtual device with uinput and forward events”.

    I’m finding difficult to do the proper setup for the virtual device to be a touch screen. I am hopping someone here can point me in the right direction…

    This is what i’ve got,

    fd = open(“/dev/uinput”, O_WRONLY | O_NONBLOCK);
    if (fd < 0) {
    die("error: open");
    }
    if(ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0)
    die("error: ioctl");

    if(ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0)
    die("error: ioctl");
    if(ioctl(fd, UI_SET_RELBIT, ABS_X) < 0)
    die("error: ioctl");
    if(ioctl(fd, UI_SET_RELBIT, ABS_X) < 0)
    die("error: ioctl");

    memset(&uidev, 0, sizeof(uidev));
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-sample");
    uidev.id.bustype = BUS_USB;
    uidev.id.vendor = 0x1;
    uidev.id.product = 0x1;
    uidev.id.version = 1;

    if (write(fd, &uidev, sizeof(uidev)) < 0) {
    die("error: write");
    }

    if (ioctl(fd, UI_DEV_CREATE) < 0) {
    die("error: ioctl");
    }

    Thank you for your time.

  52. After i create the device it is automaticly removed and i have no idea why.

    Removing device ‘/dev/input/event6’ due to inotify event

  53. I’m currently developing a device which reads external information(such as motion) and convert it to touch motion in Android.(For Wearable Computer Competition 2013)
    Thankfully, due to your work, I could realize such device.
    However, I’m currently in trouble.

    Although the injection do work for key, touch injection work only partially.
    Without pressing the screen(at least in the corner), the injected touch(which is given continuously) is NOT injected at all.
    (adb getevent doesn’t show any event injected, although the LogCat (using intEnableDebug) shows that the function intSendEvent is done.

    The intSendEvent I sent per (injected) touch is the following.

    intSendEvent(m_fd, EV_ABS,57,29);
    intSendEvent(m_fd, 3, 53,x);
    intSendEvent(m_fd, 3, 54,y);
    intSendEvent(m_fd, 3, 48,100);
    intSendEvent(m_fd, EV_ABS, 58,2);
    intSendEvent(m_fd,0,0,0);
    intSendEvent(m_fd,3,57,0xffffff);
    intSendEvent(m_fd,0,0,0);
    (Ah, EV_ABS=3)
    (In my case, m_fd is /dev/input/event2 for touchscreen)

    Also, for unknown reason, without modifying the JNI C line into
    void Java_(…)intSendEvent( JNIEnv* env,jobject thiz, int fd_kb, uint16_t type, uint16_t code, uint16_t value)
    (changed value’s type from int32_t to uint16_t)
    the value is fixed to 0.

    Do you have any possible idea why such problems arise?
    In my opinion,
    1(and Worst). The phone manufacturer had set another mechanism which prevents Android to recognize changes in /dev/input/eventX without really activating touchscreen.
    2. I set the sequence of events very very wrong.(Much better though 🙂 )

    The device I currently use is
    Galaxy S4 LTE-A (SHV-E330S in Korea but probably similar to GT-i9506 (same spec)).

    Thank you for reading this ^^
    and Sorry for my bad english.
    Ps. I emailed this but I decided to repost this whether your email address is correct. Sorry 🙁

  54. @Hyeongkeun kim: I have also implemented a “monitor” function exactly for the kind of problems you ran into: start it, and then tap the screen to see what messages are being generated. You will then need to repeat the process from your code, but using the SendEvent api. This should be easy.

  55. Is the monitor function equivalent to looking at data output from
    Adb shell getevent (-l) | grep /dev/input/event2?

  56. Hello Radu, these two part articule is great, after reading carefully i have only one question:
    How can i translate from a MotionEvent and create an event using your api with all the basic data of a MotionEvent?
    In others works how to translate every value from a MotionEvent object into values that inject the correct event using your api?
    Can you give me some tips?
    The scene i have is an app that catch events in an android movil terminal and transmit those values to other android terminal in MotionEvent format, so in this last terminal i need to inject the received events.

    Thank you very much,
    Maykell.

  57. Maykell, you might want to check out the source of MotionEvent in the Android open source, for that.

    Eric, is your phone rooted?

  58. Hi,Thanks your job for this project.I just using cygwin to compile you jni code, but fail,Most of the error was happen in the EventInjector.h ,Do u have something code forgot to commit to googleCode?

  59. $ /cygdrive/e/NDK/android-ndk-r8e/ndk-build
    Cygwin : Generating dependency file converter script
    Compile thumb : EventInjector <= EventInjector.c
    In file included from E:/soft/jni/EventInjector.c:55:0:
    E:/soft/jni/EventInjector.h:43:9: error: 'INPUT_PROP_POINTER' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:44:9: error: 'INPUT_PROP_DIRECT' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:45:9: error: 'INPUT_PROP_BUTTONPAD' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:46:9: error: 'INPUT_PROP_SEMI_MT' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:69:9: error: 'SYN_MT_REPORT' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:69:9: error: initializer element is not constant
    E:/soft/jni/EventInjector.h:69:9: error: (near initialization for 'syn_labels[2].value')
    E:/soft/jni/EventInjector.h:70:9: error: 'SYN_DROPPED' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:70:9: error: initializer element is not constant
    E:/soft/jni/EventInjector.h:615:9: error: initializer element is not constant
    E:/soft/jni/EventInjector.h:615:9: error: (near initialization for 'abs_labels[26].value')
    E:/soft/jni/EventInjector.h:718:9: error: (near initialization for 'mt_tool_labels[0].value')
    E:/soft/jni/EventInjector.h:719:9: error: 'MT_TOOL_PEN' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:719:9: error: initializer element is not constant
    E:/soft/jni/EventInjector.h:719:9: error: (near initialization for 'mt_tool_labels[1].value')
    E:/soft/jni/EventInjector.h:720:9: error: 'MT_TOOL_MAX' undeclared here (not in a function)
    E:/soft/jni/EventInjector.h:720:9: error: initializer element is not constant
    E:/soft/jni/EventInjector.h:720:9: error: (near initialization for 'mt_tool_labels[2].value')
    /cygdrive/e/NDK/android-ndk-r8e/build/core/build-binary.mk:269: recipe for target `/cygdrive/e/soft/obj/local/armeabi/objs/EventInjector/EventInjector.o' failed
    make: *** [/cygdrive/e/soft/obj/local/armeabi/objs/EventInjector/EventInjector.o] Error 1
    zhangkai@zhangkai-PC /cygdrive/e/soft/jni
    $ cd d:
    zhangkai@zhangkai-PC /cygdrive/d
    $ cd ttt
    zhangkai@zhangkai-PC /cygdrive/d/ttt
    $ cd jni
    zhangkai@zhangkai-PC /cygdrive/d/ttt/jni
    $ /cygdrive/e/NDK/android-ndk-r8e/ndk-build
    /cygdrive/e/NDK/android-ndk-r8e/build/core/add-application.mk:128: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in /cygdrive/d/ttt/AndroidManifest.xml
    /cygdrive/d/ttt/obj/local/armeabi/objs/EventInjector/EventInjector.o.d:1: *** STOP
    zhangkai@zhangkai-PC /cygdrive/d/ttt/jni
    $ /cygdrive/e/NDK/android-ndk-r8e/ndk-build
    /cygdrive/e/NDK/android-ndk-r8e/build/core/add-application.mk:128: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in /cygdrive/d/ttt/AndroidManifest.xml
    Cygwin : Generating dependency file converter script
    Compile thumb : EventInjector <= EventInjector.c
    In file included from D:/ttt/jni/EventInjector.c:55:0:
    D:/ttt/jni/EventInjector.h:43:9: error: 'INPUT_PROP_POINTER' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:44:9: error: 'INPUT_PROP_DIRECT' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:45:9: error: 'INPUT_PROP_BUTTONPAD' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:46:9: error: 'INPUT_PROP_SEMI_MT' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:69:9: error: 'SYN_MT_REPORT' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:69:9: error: initializer element is not constant
    D:/ttt/jni/EventInjector.h:69:9: error: (near initialization for 'syn_labels[2].value')
    D:/ttt/jni/EventInjector.h:70:9: error: 'SYN_DROPPED' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:70:9: error: initializer element is not constant
    D:/ttt/jni/EventInjector.h:70:9: error: (near initialization for 'syn_labels[3].value')
    D:/ttt/jni/EventInjector.h:194:9: error: 'KEY_SCALE' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:194:9: error: initializer element is not constant
    D:/ttt/jni/EventInjector.h:194:9: error: (near initialization for 'key_labels[119].value')
    D:/ttt/jni/EventInjector.h:228:9: error: 'KEY_SCREENLOCK' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:228:9: error: initializer element is not constant
    D:/ttt/jni/EventInjector.h:228:9: error: (near initialization for 'key_labels[153].value')
    D:/ttt/jni/EventInjector.h:275:9: error: 'KEY_DASHBOARD' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:275:9: error: initializer element is not constant
    D:/ttt/jni/EventInjector.h:275:9: error: (near initialization for 'key_labels[200].value')
    D:/ttt/jni/EventInjector.h:308:9: error: 'KEY_BLUETOOTH' undeclared here (not in a function)
    D:/ttt/jni/EventInjector.h:720:9: error: (near initialization for 'mt_tool_labels[2].value')
    /cygdrive/e/NDK/android-ndk-r8e/build/core/build-binary.mk:269: recipe for target `/cygdrive/d/ttt/obj/local/armeabi/objs/EventInjector/EventInjector.o' failed
    make: *** [/cygdrive/d/ttt/obj/local/armeabi/objs/EventInjector/EventInjector.o] Error 1

  60. Hi
    first at all sorry for my english
    Radu, thank you for your library
    I am trying to send touch event.
    Here are log from adb getevent
    +—————————————-
    /dev/input/event1: 0003 0035 0000015b
    /dev/input/event1: 0003 0036 0000012f
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0003 0039 ffffffff -touch up
    /dev/input/event1: 0000 0000 00000000
    +—————————————-

    and from Eclipse logcat
    +—————————————-
    Event:ct36x_ts:3 53 347
    Event:ct36x_ts:3 54 303
    Event:ct36x_ts:0 0 0
    Event:ct36x_ts:3 57 -1 -touch up
    Event:ct36x_ts:0 0 0
    +————————————-

    I mofifyed “SendTouchDownAbs” according with my options

    +————————————————————–
    public int SendTouchDownAbs(int x, int y ) {
    intSendEvent(m_nId, EV_ABS, 53, x);
    intSendEvent(m_nId, EV_ABS, 54, y);
    intSendEvent(m_nId, 0, 0, 0);
    intSendEvent(m_nId, EV_ABS, 57, -1); //touch up
    intSendEvent(m_nId, 0, 0, 0);
    return 1;
    }
    +————————————————————–

    but it works only for touch down, no touch up (release) and the log gives only
    +————————————————————–
    /dev/input/event1: 0003 0035 0000015b
    /dev/input/event1: 0003 0036 0000012f
    /dev/input/event1: 0000 0000 00000000
    +————————————————————–
    without the relese comands

    As you can see the action touch up (release) has a -1 or 0xffffffff. I tried both of them. If I put 0 insteaad “-1” the log gives
    +—————————————-
    /dev/input/event1: 0003 0035 0000015b
    /dev/input/event1: 0003 0036 0000012f
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0003 0039 00000000
    /dev/input/event1: 0000 0000 00000000
    +—————————————-
    but there is still no touch up (release).

    Can you help me with it?
    Thank you

  61. Hi Igor,

    There’s been some time since I wrote this, and do not remember all the details, but what you should do is to use the Event Monitor function, included in the sample I’ve provided. First carefully analyze all the messages (similar to what you posted: getevent/eclipse log), then try to inject exactly the same sequences.

    If you managed to get it working for touch down, it will work for touch up as well, just get the sequence right.

  62. Hi,
    why the sequence are different in getevent “0003 0039 ffffffff” and in eclipse log “3 57 -1”? I mean the last value “0xffffffff” and “-1”. I have tried to use both of them in my function with no effect.

  63. Hi Radu, is there any way to normalize the codes injected so we could write a an event injector that works with all devices ?
    I mean, are the differences caused by devices (hardware) or android versions ?
    If it’s just android version , it would be easy to implement it , but the other way is out of reach.

  64. Hi

    Big fan of this lib. I’ve used and extended it for many devices, but have now stumbled into a question that I cannot sovle without some feedback.

    Some webpages utilize mouse hover effects and I would like to be able to inject such events to a Android WebView. I obviously cannot do this using the touch injection of this lib since there is no such thing as ‘hover touch’ on android. But I am guessing that it is possible somehow since a cursor appears if I connect a mouse to a Android tablet and the hover effect happens in the WebView if I hover this mouse cursor over affected areas.

    Any idea how to use your lib for injecting mouse hover/movement events?

    BTW, I’ve tried ‘opening’ the mouse Input Devs on a few devices with no success. So I am unable to get any useful output.

    Thx in advance.

  65. Hi Radu,
    Any idea how to use your lib for touch events without external devices.
    Thank you in advance.

  66. Hi Radu,

    I’m facing a problem with this library on my Samsung Galaxy S4. Chmod of the event works but still it fails to write anything to a particular event.

    Seems like an issue due to KNOX. On other devices it works. Can you please help me solve the issue? Your help will be highly appreciated.

    Thanks.

  67. Anders,
    Did you get any success in having mouse event like moving mouse with EV_ABS event?
    I am not see any EV_ABS event in getevent output even when “Touch” pressed. Is it that EV_ABS specifically meant for touch input devices only. Generally I saw all movements of mouse as EV_REL and EV_KEY with BTN_LEFT for mouse click.
    So mouse click is working for me but no success in jumping to absolute co-ordinates. Please note, mine is not a touch based Android device. It is a simple Android media box which does not have touch panel. We generally use External Mouse and Keyboard for the operating the same.
    Hope to get some news.

  68. Hi Rado, Anders,
    Any suggestion regarding above query. I need to create new touch based eventX in /dev/input/eventX virtually without having physical connection to a touch device. Is it possible to do it. Please note, mine is media player (Android box) which does not come with touch display.
    Many thanks,
    Trushal

  69. Hi everybody,
    First thank you for all the code and all theses explanations. This is really a good work.
    I can not get why I have the following problem:
    When I click on SCAN INPUT DEVS I receive an error that dir = opendir(dirname) return NULL. Apparently the permission is denied ( i printed errno). I have a mobile that has root, I tried to chmod 666 /dev/input, I tried to make the application a root app but nothing worked. I don’t understand because it should only list the elements inside input/ as ls does.
    Can someone help me? Thank you.
    Jéremy

Leave a Reply