這是實際工作中遇到問題:要求可以在設定中修改滑鼠上的Left、Middle、Right Button對應的功能,功能有4種:左鍵點擊,右鍵點擊,菜單鍵、返回鍵。按鍵和功能的對應關係可以用系統屬性來儲存,Setting介面可以用RadioButton來完成,這些沒什麼可說的。下面要記錄的是滑鼠按鍵是如何更改功能的,在這之前先回顧一下基礎知識——輸入事件的處理流程。
首先從Kernel傳遞上來的索引值由EventHub進行轉碼,之後由InputReader將其解釋成各個事件,再由InputDispatcher分發。InputManager是InputReader和InputDispatcher線程的建立者,它只有一個職責,就是被WindowManagerService使用,從Native層擷取按鍵事件。WindowManagerService則負責與視窗對接,分發按鍵訊息。有很多文章對於按鍵事件處理講的很詳細,不贅述了,記錄下源碼位置,以後有時間慢慢看:
事件分發給最前面的視窗:
/frameworks/base/services/java/com/android/server/WindowManagerService.java
攔截訊息的處理類:
/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
按鍵事件定義:
/frameworks/base/core/java/android/view/KeyEvent.java
Java層輸入管理:
/frameworks/base/services/java/com/android/server/InputManager.Java
native層輸入管理:
/frameworks/base/libs/ui/InputManager.cpp
事件讀取線程:
/frameworks/base/libs/ui/InputReader.cpp
事件分發線程:
/frameworks/base/libs/ui/InputDispatcher.cpp
鍵碼與索引值轉換:
/frameworks/base/libs/ui/EventHub.cpp
分析過處理流程,可以發現要修改滑鼠按鍵對應的功能,應該從InputReader入手——發生了什麼事件,都是由 InputReader決定的,RawEvent只負責丟上來碼值,InputDispatcher只負責把InputReader翻譯出來的事件傳達一下,我們讓InputReader撒個謊,把左鍵事件說成右鍵事件,右鍵事件說成中間鍵事件,不就搞定了嗎?
思維過程記錄完畢,其實到這裡問題已經找到解決方案了。接下來是策反InputReader的具體方法,上代碼:
int mMouseKeySettingSupport=1; //表示是否有設定滑鼠鍵的功能,1有,0無。可以從屬性中將其讀取出來,讀取過程不贅述,直接設定為1.static bool isPointerDown(int32_t buttonState) {if(mMouseKeySettingSupport){// 這個函數是判斷是否有指標被按下,例如點擊滑鼠左鍵、右鍵,都屬於點擊事件。因為等一下AMOTION_EVENT_BUTTON_TERTIARY要作為Menu鍵,所以取消其作為點擊使用。return buttonState & (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY);}else{return buttonState &(AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY| AMOTION_EVENT_BUTTON_TERTIARY);}}static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,nsecs_t when, int32_t deviceId, uint32_t source,uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,lastButtonState, currentButtonState,AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,lastButtonState, currentButtonState,AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);//支援菜單鍵if(mMouseKeySettingSupport){synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,lastButtonState, currentButtonState,AMOTION_EVENT_BUTTON_TERTIARY, AKEYCODE_MENU);}}//以下是按鍵映射函數,用於讀取系統屬性擷取按鍵對應功能enum{MOUSE_BTN_LEFT =0,MOUSE_BTN_MIDDLE,MOUSE_BTN_RIGHT,MOUSE_BTN_COUNT};enum{MOUSE_KEY_CLICK = 0,MOUSE_KEY_MENU,MOUSE_KEY_BACK,MOUSE_KEY_RIGHT,MOUSE_KEY_NUM};uint32_t CursorButtonAccumulator::getButtonMapValue(uint32_t key) const{if(key >= MOUSE_BTN_COUNT){return 0;}const char *properties[] = {"persist.sys.mouse.btn.left","persist.sys.mouse.btn.middle","persist.sys.mouse.btn.right"};const struct stMouseBtnFunc{char propValue[16];uint32_t func;}functions[]={{"click", AMOTION_EVENT_BUTTON_PRIMARY},{"menu", AMOTION_EVENT_BUTTON_TERTIARY},{"back", AMOTION_EVENT_BUTTON_BACK},{"right", AMOTION_EVENT_BUTTON_SECONDARY}};uint32_t defaultFunc[]={AMOTION_EVENT_BUTTON_PRIMARY, AMOTION_EVENT_BUTTON_TERTIARY, AMOTION_EVENT_BUTTON_BACK};const char* UNKONW_KEYVALUE="unknow";char *propValue=new char[16];property_get(properties[key], propValue,UNKONW_KEYVALUE);if(strcmp(propValue,UNKONW_KEYVALUE)!=0){for(int i=0; i<MOUSE_KEY_NUM;i++){if(strcmp(propValue,functions[i].propValue)==0){return functions[i].func;}}}return defaultFunc[key];}//偷梁換柱,按鍵對應的索引值用getButtonMapValue擷取uint32_t CursorButtonAccumulator::getButtonState() const {uint32_t result = 0;if (mBtnLeft) {if(mMouseKeySettingSupport){result |= getButtonMapValue(MOUSE_BTN_LEFT); //左鍵}else{result |= AMOTION_EVENT_BUTTON_PRIMARY; }ALOGD("getButtonState. result=%d.", result);}if (mBtnOk1||mBtnOk2) {result |= AMOTION_EVENT_BUTTON_PRIMARY;}if (mBtnRight) {result |= AMOTION_EVENT_BUTTON_BACK;//result |= AMOTION_EVENT_BUTTON_SECONDARY;}if (mBtnMiddle) {if(mMouseKeySettingSupport){result |= getButtonMapValue(MOUSE_BTN_MIDDLE); //中間鍵}else{result |= AMOTION_EVENT_BUTTON_TERTIARY;}ALOGD("getButtonState. result=%d.", result);}if (mBtnBack || mBtnSide) {if(mMouseKeySettingSupport){result |= getButtonMapValue(MOUSE_BTN_RIGHT); //右鍵}else{result |= AMOTION_EVENT_BUTTON_BACK;}}if (mBtnForward || mBtnExtra) {result |= AMOTION_EVENT_BUTTON_FORWARD;}return result;}
如果對於framework中輸入訊息處理的流程足夠熟悉,實現功能是個很簡單的過程。在遇到問題的時候,如果沒有頭緒,不妨先把周邊的知識都瞭解清楚再尋找解決方案。解決問題的能力遠比解決問題本身重要得多。