android跨進程事件注入(程序模擬用戶輸入,getevent與sendevent)

轉自http://zuoshu.iteye.com/blog/1775606


早想寫這篇,一直沒空,現在總結下。
需求:
需要在程序內模擬用戶輸入,比如點擊屏幕,或者輸入鍵盤。模擬用戶的滑動等。具體的需求,比如測試的時候,測試打開瀏覽器1000次。或者通過網絡發送命令給手機,在手機上執行點擊或者輸入。再或者,平板和藍牙鼠標通過藍牙通信,通過鼠標讓平板上的鼠標能移動和點擊。這些都需要用到事件注入。
分析:
模擬用戶輸入的方式有幾種,一是monkeyrunner,這個的原理是在PC上,通過python調用android的一些包,然後通過機器的調試端口和機器通信,機器接收到相應的命令後再往硬件寫入相應的事件。這個常用語測試。並且,不是所有的機器都開了調試端口,並且需要連接PC。二是IwindowManager的injectInputEventNoWait,這個調用方便,也很簡單,但是從1.5(1.6?)後android系統做了限制,不允許跨進程注入,這個方法只能在自己這個程序內用,home出去就不行了。三是直接往linux底層/dev/input/event*寫事件,這個實現起來複雜,需要root權限,但是卻能實現跨進程,比如藍牙鼠標的需求,也只能用這種方法實現。講這個具體實現的不多,本文詳細介紹下。對linux瞭解些的人應該一看就懂知道怎麼回事。android上實現只不過有些地方比較繞而已。
1.android界面點擊事件流程。
有必要先說下android界面捕獲事件的流程。用戶在屏幕上點擊一下後,程序裏面的OnClickListener是怎樣收到這個事件的。大致流程如下
用戶點擊-(硬件驅動部分)硬件產生一箇中斷,往/dev/input/event*寫入一個相應的信號->jni部分,android循環讀取/dev/input/event*的事件,再分發給WindowManagerServer,最後再發到相應的ViewGroup和View。這裏可以通過往/dev/input/event*寫信號的方式,來達到模擬事件的目的,接下來關心的就是信號的協議了。
2.按鍵協議分析
連接手機,adb shell,輸入getevent,關掉手機的自動旋轉屏幕,按一下手機的menu鍵,會看到類似如下輸出。


linux上的硬件會分別對應/dev/input/event*,這裏的*一般是0-9的數字,getevent開頭那部分已經顯示,event2是keypad,event1是touchscreen等。
最下面的0001 008b 00000001分別叫做type,code,value。
參考linux input,type 對應 【#define EV_KEY 0x01】,code 對應【#define KEY_MENU 139】(8b == 139),value 1表示按下,0表示鬆開。那麼按鍵的協議就很清楚了,試着在adb shell裏面輸入“sendevent /dev/input/event2 0 139 1”和”sendevent /dev/input/event2 0 139 2“後發現menu彈出來了,和按鍵的效果一樣。
3.觸摸協議分析
ok,來點複雜的。觸摸協議稍微麻煩點,分單點觸摸和多點觸摸。
先說單點觸摸,打開模擬器。同樣關閉自動旋屏,進入adb shell。鼠標點擊一下屏幕,要足夠快,不然數據太多。得到輸出和下面類似。

 
可以看到模擬器上的設備數少了很多,單點觸摸的協議每次點擊會寫6條信號。參考linux_input對應的值以及分析分別如下 
/dev/input/event0: 0003 0000 00000117   EV_ABS  ABS_X  0x117
觸摸點的x座標
/dev/input/event0: 0003 0001 0000020f    EV_ABS  ABS_Y  0x20f
觸摸點的y座標
/dev/input/event0: 0001 014a 00000001   EV_KEY  BTN_TOUCH 1
touch down
/dev/input/event0: 0000 0000 00000000   EV_SYN 0 0 
同步信號量
/dev/input/event0: 0001 014a 00000000   EV_KEY  BTN_TOUCH 0
touch up
/dev/input/event0: 0000 0000 00000000   EV_SYN 0 0

同步信號量
使用4.0的模擬器,settings-developer options-show touches 和pointer locations勾上後,可以看到點擊的軌跡,adb shell後分別用sendevent輸入以上消息,可以看到屏幕上出現點擊效果。
再看多點觸摸協議,使用adb shell 進入手機,關掉旋屏,getevent後快速點一下屏幕,可以看到類似如下輸出。(每個廠商的協議可能不同,以下數據爲小米1的)
第一個座標
/dev/input/event1: 0003 0039 00000000 EV_ABS ABS_MT_TRACKING_ID 0
/dev/input/event1: 0003 0035 000001b0 EV_ABS ABS_MT_POSITION_X 0x1b0
/dev/input/event1: 0003 0036 000000d7 EV_ABS ABS_MT_POSITION_Y 0xd7
/dev/input/event1: 0003 003a 00000001 EV_ABS  ABS_MT_PRESSURE 0x1
/dev/input/event1: 0003 0032 00000001 EV_ABS ABS_MT_WIDTH_MAJOR 0x1
/dev/input/event1: 0000 0002 00000000 EV_SYN SYN_MT_REPORT 0
/dev/input/event1: 0000 0000 00000000 EV_SYN SYN_REPORT 0
第二個座標
/dev/input/event1: 0003 0039 00000000 EV_ABS ABS_MT_TRACKING_ID 0
/dev/input/event1: 0003 0035 000001b0 EV_ABS ABS_MT_POSITION_X 0x1b0
/dev/input/event1: 0003 0036 000000d7 EV_ABS ABS_MT_POSITION_Y 0xd7
/dev/input/event1: 0003 003a 00000001 EV_ABS  ABS_MT_PRESSURE 0x1
/dev/input/event1: 0003 0032 00000001 EV_ABS ABS_MT_WIDTH_MAJOR 0x1
/dev/input/event1: 0000 0002 00000000 EV_SYN SYN_MT_REPORT 0
/dev/input/event1: 0000 0000 00000000 EV_SYN SYN_REPORT 0
第三個座標
/dev/input/event1: 0003 0039 00000000 EV_ABS ABS_MT_TRACKING_ID 0
/dev/input/event1: 0003 0035 00000191 EV_ABS ABS_MT_POSITION_X 0x191
/dev/input/event1: 0003 0036 00000098 EV_ABS ABS_MT_POSITION_Y 0x98
/dev/input/event1: 0003 003a 00000001 EV_ABS  ABS_MT_PRESSURE 0x1
/dev/input/event1: 0003 0032 00000001 EV_ABS ABS_MT_WIDTH_MAJOR 0x1
/dev/input/event1: 0000 0002 00000000 EV_SYN SYN_MT_REPORT 0
/dev/input/event1: 0000 0000 00000000 EV_SYN SYN_REPORT 0
鬆開
/dev/input/event1: 0000 0002 00000000 EV_SYN SYN_MT_REPORT 0
/dev/input/event1: 0000 0000 00000000 EV_SYN SYN_REPORT 0
這裏是一次點擊,注意到ABS_MT_TRACKING_ID都是一樣的。系統檢測到三個點,每次會發送點的x,y,以及收到的壓力,觸摸的範圍。最後兩條表示鬆開這個點。如果兩個手指同時點擊,可以發現ABS_MT_TRACKING_ID會有兩個不同的值,分別是兩個點。據說最多支持5點。
每個廠商實現協議不一樣。htc g3如下
/dev/input/event1: 0003 003a 002a0002
/dev/input/event1: 0003 0039 8b8c0ddc
/dev/input/event1: 0003 003a 00000002
/dev/input/event1: 0003 0039 8bac0dde
/dev/input/event1: 0003 003a 00000000
/dev/input/event1: 0003 0039 802814b1

4.可能遇到的問題

實際實現的時候,還可能遇到問題

一是root,getevent和sendevent需要/dev/input/event*的權限。一般應用是沒有這個權限的,需要在程序裏面獲取su後,執行chmod 666 /dev/input/event*。

二是設備名稱。因爲你不知道觸摸屏或者按鍵到底對應的event*是多少。需要有一個初始化的過程,大致思路是往event0-event9分別寫入按鍵和觸摸信號,同時監聽activity裏的onkeydown和view的onclick,這樣來偵測設備。

三是廠商的實現不一樣,這個沒辦法,只能一個一個適配了,一般來說都還是標準的,有些廠商會有單獨的實現。
參考
http://lxr.free-electrons.com/source/include/uapi/linux/input.h#L803
http://source.android.com/tech/input/touch-devices.html
http://cjix.info/blog/misc/internal-input-event-handling-in-the-linux-kernel-and-the-android-userspace/

發佈了33 篇原創文章 · 獲贊 40 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章