Monkey源码分析番外篇之Android注入事件的三种方法比较

最后更新于:2022-04-01 19:56:59

原文:http://www.pocketmagic.net/2012/04/injecting-events-programatically-on-android/#.VEoIoIuUcaV 往下分析monkey事件注入源码之前先了解下在android系统下事件注入的方式,翻译一篇国外文章如下。 ## Method 1: Using internal APIs 方法1:使用内部APIs This approach has its risks, like it is always with internal, unpublished APIs. 该方法和其他所有内部没有向外正式公布的APIs一样存在它自己的风险。 The idea is to get an instance of WindowManager in order to access the injectKeyEvent / injectPointerEvent methods. 原理是通过获得WindowManager的一个实例来访问injectKeyEvent/injectPointerEvent这两个事件注入方法。 ~~~ IBinder wmbinder = ServiceManager.getService( "window" ); IWindowManager m_WndManager = IWindowManager.Stub.asInterface( wmbinder ); ~~~ The ServiceManager and WindowsManager are defined as Stubs. We can then bind to these services and call the methods we need.  ServiceManager和Windowsmanager被定义为存根Stubs类。我们根据我们的需要绑定上这些服务并访问里面的方法。 To send a key do the following: 通过以下方式发送一个事件: ~~~ // key down m_WndManager.injectKeyEvent( new KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A ),true ); // key up m_WndManager.injectKeyEvent( new KeyEvent( KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A ),true ); ~~~ To send touch/mouse events use: 发送touch/mouse事件: ~~~ //pozx goes from 0 to SCREEN WIDTH , pozy goes from 0 to SCREEN HEIGHT m_WndManager.injectPointerEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),MotionEvent.ACTION_DOWN,pozx, pozy, 0), true); m_WndManager.injectPointerEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),MotionEvent.ACTION_UP,pozx, pozy, 0), true); ~~~ This works fine, but only inside your application 这种方法能在你的应用中很好的工作,但,也仅仅只能在你的应用中而已 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-15_57b1755c14f98.jpg) The moment you're trying to inject keys/touch events to any other window, you'll get a force close because of the following exception: 一旦你想要往其他窗口注入keys/touch事件,你将会得到一个强制关闭的消息: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-15_57b1755c305a2.jpg) ~~~ E/AndroidRuntime(4908): java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission ~~~ Not much joy, as INJECT_EVENTS is a system permission. A possible solution is discussed [here](http://stackoverflow.com/questions/3598662/how-to-compile-android-application-with-system-permissions) and [here](http://stackoverflow.com/questions/5383401/android-inject-events-permission). 苦逼了吧,毕竟INJECT_EVENTS是需要系统权限的,一些可能解决的方案在[这里](http://stackoverflow.com/questions/3598662/how-to-compile-android-application-with-system-permissions)和[这里](http://stackoverflow.com/questions/5383401/android-inject-events-permission)有讨论到。 (译者注:请查看本人上一篇翻译的《[Monkey源码分析番外篇之WindowManager注入事件如何跳出进程间安全限制](http://blog.csdn.net/zhubaitian/article/details/40428097)》里面有更详细针对这个问题的描述) ## Method 2: Using an instrumentation object **方法2: 使用instrumentation对象** This is a clean solution based on public API, but unfortunately it still requires that INJECT_EVENTS permission. 相对以上的隐藏接口和方法,这个是比较干净(上面的是隐藏的,故需要用到android不干净不推荐的方法去获取)的方式,但不幸的事它依然有上面的JINECT_EVENTS这个只有系统应用(基本上就是android自己提供的,如monkey)才被允许的权限问题。 ~~~ Instrumentation m_Instrumentation = new Instrumentation(); m_Instrumentation.sendKeyDownUpSync( KeyEvent.KEYCODE_B ); ~~~ For touch events you can use: 以下是触摸事件实例: ~~~ //pozx goes from 0 to SCREEN WIDTH , pozy goes from 0 to SCREEN HEIGHT m_Instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),MotionEvent.ACTION_DOWN,pozx, pozy, 0); m_Instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),MotionEvent.ACTION_UP,pozx, pozy, 0); ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-15_57b1755c4767d.jpg) All good inside the test application, and will crash instantly when trying to inject keys to outside apps, not because the approach doesn't work, but because Android Developers have chosen so. Thanks guys, you rock! Not. 在应用内操作的话完全没有问题,但一旦跳出这个应用去触发按键事件的话就会崩溃。不是因为这个方法不工作,而是因为android开发人员做了限制。谢谢你们,android的开发者们,你牛逼!个屁。 By looking at sendPointerSync's code, you will quickly see it uses the same approach as presented in method 1). So this is the same thing, but packed nicely in a easy to use API: 通过分析sendPointerSync的对应代码,可以看到其实instrumentation使用到的注入事件方式其实和方法一提到的通过WindowManager.injectPointerEvents是一样的,所以穿的都是同一条内裤,只是Robotium出来走动的时候套上条时尚喇叭裤,而以上直接调用WindowManager的方式就犹如只穿一条内裤出街的区别而已。 ~~~ public void sendPointerSync(MotionEvent event) { validateNotAppThread(); try { (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))) .injectPointerEvent(event, true); } catch (RemoteException e) { } } ~~~ ## Method 3: Direct event injection to /dev/input/eventX **方法3:直接注入事件到设备/dev/input/eventX** Linux exposes a uniform input event interface for each device as /dev/input/eventX where X is an integer. We can use it directly and skip the above Android Platform permission issues. linux以系统设备的方式向用户暴露了一套统一的事件注入接口/dev/input/eventX(其中X代表一个整数)。我们可以直接跳用而跳过以上的平台(android这个机遇linux的平台)限制问题。 For this to work, we will need root access, so this approach only works on a rooted device. 但是这需要工作的话,你需要rooted过的设备。 By default the eventX files have the permission set for 660 (read and write for Owner and Group only). To inject keys from our application, we need to make it writable. So do this first: 设备文件eventX默认是被设置为660这个权限的(Owner和同组成员有读写,而owner是root)。为了向这个设备注入事件,你必须让它能可写。所以请先做以下动作: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-15_57b1755c605bc.jpg) ~~~ adb shell su chmod 666 /dev/input/event3 ~~~ You will need root to run the chmod command. 你将需要root权限来运行chmod命令。
 

作者

自主博客

微信

CSDN

天地会珠海分舵

http://techgogogo.com


服务号:TechGoGoGo

扫描码:

http://blog.csdn.net/zhubaitian


';