Android Mutlitouch Input Architecture

 

image

現在的任務是要在Android 上面啟用 Mutlitouch的功能,首先找了一下網路上的做法,要改Synaptics的Driver,因為G1是使用Snaptics的觸控版,在別人patch過的很明顯可以看到Driver可以拿到兩個座標,可是Android所定義的 MotionEvent只能傳遞X, Y, Pressure, Size,Size的定義是觸控版手指頭上面壓下去之後的圈圈大小,這個值可能沒甚麼用,人家就拿來給Mutlitouch應用了。

先說明修改過之後 User看到的是甚麼,如果收到的Size <=1 的話,那麼就是代表是 Single Touch,否則 X, Y 就是兩個點的中心點,Size的左 16 bit 則是 abs(x1-x2), 右 16 bit則是 abs(y1-y2),這樣在User Level就可以得到旋轉跟放大縮小。

目前只有iPhone的SDK是可以直接把點座標全部傳給User Level去,這樣如果要作多人遊戲的話,才可以使用座標,特地看了一下Windows 7 的SDK,看起來在 .Net 新增的 NameSpace: Windows7.Mutlitouch 也是沒有傳遞多點座標,只有Rotate 跟 Zoom 的事件,我沒有實際寫程式試試,可能有辦法取得座標也不一定。

透過Synaptics 的source 發現他是使用

input_report_abs(ts->input_dev, ABS_X, x); 來傳遞值的,所以就找ABS_X是在哪裡定義的,在linux/include/linux/input.h 下面有定所有輸入的值為何,可以看到絕對值系列還有一個ABS_RX,原本是給搖桿用的,但透過這個值,我們應該可以傳遞兩個座標了。

Driver回報上去之後,User Level要怎麼接收呢?我以為是某一個類似scanf的函式,找到就OK了,freedom問file descriptor 是甚麼?我才想到把file operations串起來的就是要透過file descriptor,試了一下G1,發現他的TouchPanel 是 /dev/input/event2,所以我就在Android Source裡面搜尋 /dev/input,發現了 EventHub.cpp 這個檔案,在此檔案裡面搜尋一下有沒有讀 ABS_X的Code,沒想到竟然沒有,但整個frameworks也沒有其他地方會開啟/dev/input啦。再試一下 Touch,喔~看到東西了,會根據下層傳上來的某個bit判斷此device是不是 TouchScreen,然後設定其 Classes 為 CLASS_TOUCHSCREEN。EventHub跟Driver中間是 Linux Input SubSystem,有興趣的可以自己查網路,有滿好的分析。

接下來要找出EventHub上面那一層,在JAVA環境要用C讀取系統資訊要透過JNI,使用EventHub.cpp 裡面的某一個函式 getAbsoluteInfo,就找到了KeyInputQueue.java 跟 com_android_server_KeyInputQueue.cpp,所以是KeyInputQueue這個Class會來跟 EventHub讀取資料,KeyInputQueue裡面有一個 run 的函式會使用while(true) 一直 readEvent,然後看到此Class有定義一個叫做 getEvent的function,應該是給上層呼叫的,就再用此為關鍵字搜尋找到 WindowManagerService,一樣也是透過一個while(true) 從 KeyInputEvent讀event上來,再根據是甚麼類型的輸入呼叫不同的dispatch[Key|Pointer|Trackball],while 裡面就找到現在的前景視窗,檢查一下權限,就把event傳給他了,我查到是 IWindow 有定義了dispatch[Key|Pointer|Trackball],但找不到是哪個class有實作,目前猜上面還有 ViewRoot 跟 View,會檢查User Program有沒有設定相關的listener,如果就傳遞data過去。

這裡比較特別的是,我以為會像底層driver一樣一次拿到byte array,然後根據第幾欄位是甚麼資料寫入相對應的欄位,沒想到在 KeyInputQueue 讀取 X, Y, Pressure, Size 的時候竟然是分次讀取

absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");

absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");

absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");

absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");

熊熊想起,Driver在將資訊往上報的時候也是一個一個報的啊,input_report_abs

而且我不應該自己搜尋source的,在 google 上搜尋 Android input 流程,就會有人家說流程是怎麼呼叫的了,結果我都快找完了才看到google,衰~

myandroid/frameworks/base/services/java/com/android/server/InputDevice.java

有把軌跡球轉成key event 的函式

接下來分析一下如果要讓我們的User Program可以讀到多點座標要怎麼辦,目前看起來 Android Framework從下面讀取的資料只有 X, Y, Pressure, Size,如果不想改Android Framework的話,那我們就要自己encode Data了,ex: 把 X左16 bit存X1, 右 16bit 存 X2,使用模擬器,除非解析度會大於65535才有問題,但要犧牲一點精確度就是了。原本X 的type 是 float。如果像我的主管要求要能支援三個點以上的話,那就麻煩了,首先Linux沒有定義那麼多Data Type,linux/include/linux/input.h,如果還要透過標準的Linux Input SubSystem的Interface的話,我覺得要改的東西還滿多的,而且還要想辦法encode 座標。像是driver一次就是回報n個點,如果沒按那麼多的話,該點就填-1,-1 之類的。然後 Android Framework 也要extend MotionEvent給他多一個ArrayList 來存多點座標。

檔案路徑:

myandroid/frameworks/base/libs/ui/EventHub.cpp

myandroid/frameworks/base/core/java/android/view/MotionEvent.java

myandroid/frameworks/base/services/java/com/android/server/KeyInputQueue.java

myandroid/frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp

myandroid/frameworks/base/services/java/com/android/server/WindowManagerService.java

myandroid/frameworks/base/core/java/android/view/View.java

google 可以搜尋的關鍵字

linux 內核輸入子系統分析、Android 輸入事件流程、Android 輸入流程分析、輸入子系統 event、Android Input Event Dispatching、Android Event 傳遞流程

留言

這個網誌中的熱門文章

好貴的東元冷氣維修--馬達啟動電容

台大醫院 婁培人 耳鼻喉科 就診

機車無法充電之整流器壞掉--$650