Simulating key presses on Android devices
I need to develop an application that is capable of communicating with another Android application to perform a few specific tasks. Ideally this should be done via public API methods exposed by the third party app, but since none of that are available, I have to resort to sending keystrokes, such as Enter and Back, to achieve the same purpose.
Believe it or not, attempting to do the same on Android proves to be rather challenging. Despite trying various methods described here such as using dispatchKeyEvent, BaseInputConnection.sendKeyEvent, Instrumentation.sendKeyDownUpSync or even executing input keyevent from the Android app as root to simulate the keystrokes, all I get was an error message “Injecting to another application requires INJECT_EVENTS permission” as soon as I tried to send the keystroke to another app while my app was in the background. This is with the INJECT_EVENTS permission correctly added to the AndroidManifest.xml and the device rooted with SuperSU to grant INJECT_EVENTS permission to my app.
Not giving up, I tried to install my app as a system app by copying its APK into the /system/app folder as well as setting its user id to 0 as described here. Editing /data/system/packages.xml from the rooted Android device or emulator (which is BlueStacks in my case) was a challenge in its own right as the file is big enough to cause most Android text editors to struggle. In the end, I used nano on Android which was able to edit the file nicely. Still, all these efforts were wasted as the error message about the INJECT_EVENTS permission persisted. I also tried to compile my app with system permission using the steps here as well as using SuperSU to convert my app to a system app to no avail. The behavior is the same across BlueStacks, Android SDK built-in emulators as well as several devices that I have. I guess somewhere deep down in the Android source code, it was hard-coded to only allow a few apps to use INJECT_EVENTS, regardless of what type of permissions or configuration other apps might have.
In a last attempt, I installed Xposed and XInstaller module on BlueStacks, thinking it would be able to allow my app to run as a system app without all the signature and security verifications that would prevent access to the INJECT_EVENTS permission. This was a big mistake. After an apparently smooth installation, BlueStacks took a very long time to boot up and when it did, launching any app on BlueStacks would just show a purple screen. No amount of troubleshooting would help and I had to reinstall BlueStacks and setup my Android system from scratch.
I also found a workaround here which suggested writing the keystrokes to /dev/input/eventX nodes directly. All you need to do is just to run chmod 666 on the event nodes, find out which event node is responsible for receiving keystrokes, and write to the event node directly. During my tests, the sample code worked fine on my rooted Android 4 device but did not work on the BlueStack emulator – writing to the event node has no effect. I did not have the time to troubleshoot this further.
In the end, I decided to achieve the purpose by writing a .NET app which uses ADB commands to send keystrokes. This is the only thing that works. All I have to do is enabling the BlueStacks ADB bridge:
With ADB, sending a keystroke to any app within BlueStacks can be done with just a simple command – no INJECT_EVENTS permissions or root access needed. The following will simulate a MENU key press:
adb shell input keyevent 82
This is the simplest solution I can think of but not the solution that I initially expected. Despite being able to achieve my objectives, I still believe sending keystrokes on Android does not have to be that complicated. On the contrary, sending keystrokes from a .NET application to other Windows applications is as simple as using SendKeys.Send() with no permissions or security configurations needed whatsoever.