InputManagerService(IMS)
Linux內核,接受輸入設備的中斷,并將原始事件的數據寫入設備節點中
設備接電,作為內核與IMS的橋梁,將原始事件的數據暴露給用戶空間,以便IMS可以從中讀取事件
InputManagerService,一個android系統服務,分為Java層和Native層兩部分,java層負責與WMS通信,而Native層則是InputReader和InputDispatcher兩個輸入系統關鍵組件的運行容器
EventHub,直接訪問所有的設備節點,通過一個名為getEvents()的函數將所有輸入系統相關的待處理的底層事件返回給使用者,包括原始輸入事件,設備節點的增刪等
InputReader,是IMS中的關鍵組件之一,它運行一個獨立的線程中,負責管理輸入設備的列表和配置,以及進行輸入事件的加工處理,它通過其線程循環不斷地通過getEvents()函數從EventHub中將事件取出并進行處理,對于設備節點的增刪事件,它會更新輸入設備列表與配置,對于原始輸入事件,InputReader對其進行翻譯,組裝,封裝為包含更多信息,更多可讀性的輸入事件,然后交給InputDispatcher進行派發
InputReaderPolicy,為InputReader的事件加工處理提供一些策略配置
InputDispatcher,是IMS中的另一個關鍵組件,運行于一個獨立的線程中,InputDispatcher中保管來自WMS的所有窗口的信息,收到InputReader的輸入事件后,會在其保管的窗口中尋找合適的窗口,并將事件派發給此窗口
InputDispatcherPolicy,為InputDispatcher的派發過程提供策略控制,例如HOME鍵被InputDispatcherPolicy截取到PhoneWindowManager中處理,并阻止窗口收到HOME鍵按下的事件
WMS,并不是輸入系統的一員,新建窗口時,WMS為新窗口和IMS創建了事件傳遞所用的通道,會將窗口的可點擊區域,焦點窗口等信息實時更新到IMS的InputDispatcher中,使得InputDispatcher可以正確將事件派發到指定窗口
ViewRootImpl,對某些窗口,如壁紙窗口,SurfaceView的窗口來說,窗口就是輸入事件派發的終點,而對其他的如Activity,對話框等使用了Android控件系統的窗口來說,輸入事件的終點是控件
IMS構成
SystemServer->ServerThread.run()
1.創建新的IMS對象
->com_android_server_input_InputManagerService.cpp->nativeInit()
創建了NativeInputManager對象,該對象實現了InputReaderPolicyInterface與InputDispatcherPolicyInterface接口,創建EventHub和InputManager,InputManager創建了InputReader與InputDispatcher以及InputReaderThread與InputDispatcherThread
2.調用IMS對象的start函數完成啟動
![Upload Paste_Image.png failed. Please try again.]
![Upload Paste_Image.png failed. Please try again.]
InputReader的總流程
InputReader.cpp->InputReaderThread::threadLoop()
分三步:
1.首先從EventHub抽取未處理的時間列表,一類是從設備節點讀取的原始輸入事件,另一類則是輸入設備可用性變化事件,簡稱設備事件
2.processEventsLocked對事件進行處理,對于設備事件,此函數對根據設備的可用性加載或移除設備對應的配置信息,對原始輸入事件,則在進行轉譯,封裝與加工后將結果暫存到mQueuedListener
3.所有事件處理完畢后,調用mQueuedListener.flush將所有暫存的輸入事件一次性地交給InputDispatcher
深入理解EventHub
1.設備節點監聽的建立
通過INotify與Epoll機制建立起對設備節點增刪事件以及可讀狀態的監聽
EventHub.cpp:EventHub
2.getEvents
使用Epoll的核心是mPendingEventItems數組,它是一個事件池,getEvents包含了原始輸入事件讀取,輸入設備加載/卸載等操作
3.輸入設備管理
每個輸入設備在dev/input/下有一個設備節點,設備節點包含輸入設備的所有信息,EventHub負責在設備節點可用時加載并維護這些信息,并在設備節點被刪除時將其移除,名為Device的私有結構體保存
4.原始輸入事件的監聽與讀取
當設備的原始輸入事件到來之時,getevents函數將會獲得一個Epoll事件,然后根據Epoll事件讀取文件描述符的原始輸入事件,將其填充到RawEvents結構體并放入buffer中被調用者取走
深入理解InputReader
1.原始輸入事件的加工
InputReader:processEventsLocked
->inputReader:processEventsForDeviceLocked
->InputReader:InputDevice::process
InputDevice描述一個輸入設備,是一個存儲輸入設備信息的類
InputMapper是InputReader中實際進行原始輸入事件加工的場所
2.InputDevice與InputMapper
inputDevice創建InputReader:addDeviceLocked
->InputReader:createDeviceLocked
3.keyboard類型事件的加工處理
(1)keyboardInputMapper配置(屏幕旋轉狀態)
(2)鍵盤掃描碼與虛擬鍵值
掃描碼是硬件實現,虛擬鍵值是操作系統實現
(3)掃描碼到虛擬鍵值的映射
InputReader->KeyboardInputMapper:process
(4)按鍵事件的加工處理
InputReader->keyboardInputMapper:processKey
4.Touch類型事件的加工處理
(1)Touch類型事件的信息與原始事件的組織方式
(2)TouchInputMapper的體系
![Upload Paste_Image.png failed. Please try again.]
(3)MultiTouchInputMapper的配置
MultiTouchInputMapper的configureRawPointerAxes獲取來自設備節點的各項觸控信息,同時構建傳感器的物理坐標系
TouchInputMapper的configureSurface獲取來自DisplayViewPort的屏幕方向以及屏幕坐標系的信息,并計算物理坐標系到屏幕坐標系的差異信息
(4)點擊事件的信息收集
InputReader->MultiTouchInputMapper:process
->MultiTouchMotionAccumulator:process
(5)點擊事件信息的整合,變換與高級事件的生成
輸入事件的派發
1.將事件注入派發隊列
InputDispatcher實現了InputListenerInterface,并在InputReader循環的最后,QueuedInputListener調用此接口將InputReader產生的事件以及NotifyXXXArgs結構體的形式提交給InputDispatcher
->InputDispatcher:notifyMotionLocked
->InputDispatcher:enqueueInboundEventLocked
2.派發線程的線程循環
InputDispatcher:dispatchOnce
派發線程的一次循環包括以下三項:
進行一次事件派發,事件的派發工作僅當命令隊列中沒有命令時才會進行,派發工作會設置nextWakeupTime指明隨后休眠時間長短
執行命令列表中的命令
陷入休眠狀態
3.派發工作的整體流程
dispatchOnceInnerLocked函數體現派發過程
InputDispatcher:dispatchOnceInnerLocked
4.事件被丟棄的原因
.....
5.Motion事件目標窗口的確定
InputDispatcher:dispatchMotionLocked
三項工作:
對于被丟棄的事件,返回true
為事件尋找合適的窗口,窗口分為普通窗口和監聽窗口,普通通過按點和焦點查找,監聽窗口則無條件監聽所有輸入事件
如果成功地找到可以接收事件的目標窗口,通過dispatchEventLocked完成實際的派發工作
6.向窗口發送事件
InputDispatcher:dispatchEventLocked
按鍵事件的派發
1.將事件注入派發隊列
2.額外的派發策略查詢
InputDispatcher:dispatchOnceInnerLocked
3.重復按鍵事件
InputDispatcher:dispatchOnceInnerLocked
->InputDispatcher:dispatchKeyLocked 開啟與關閉重復按鍵模擬
->InputDispatcher:synthesizeKeyRepeatLocked重復按鍵的生成
4.按鍵事件派發總結
按鍵事件通過notifyKey函數進入InputDispatcher,在注入派發隊列前,使用DispatcherPolicy的interceptKeyBeforeQueueing函數詢問后續的派發策略policyFlag
按鍵事件在正式派發給窗口前,進行一次額外的派發策略查詢,查詢的結果保存在keyEntry:interceptKeyResult,結果覺得事件是正常派發,稍后派發還是丟棄
當按鍵按下到按鍵抬起之間的時間里,dispatchOnceInnerLocked和dispatchKeyLocked會協同工作完成對重復按鍵事件的模擬
按鍵事件的派發目標僅通過焦點方式進行查找
輸入事件的發送,接收和反饋
InputDispatcher運行于system_server進程,窗口運行于其它的應用進程中
深入理解InputChannel
InputChannel的本質是一對SocketPair,SocketPair用來實現在本機內進行進程間的通信
InputTransport->InputChannel:openInputChannelPair
連接InputDispatcher和窗口
WinodwManagerService:addwindow
WMS添加窗口時,會創建一對InputChannel,其中一個保存在WindowState中,并注冊給IMS,它是服務端,另一個則通過傳出參數outInputChannel交給調用者,是客戶端
1.服務端連接的建立
addwindow函數中,有以下三項工作:
通過WindowState.setInputChannel函數保存服務端的InputChannel
通過IMS.registerInputChannel將InputChannel注冊到IMS
通過InputMonitor.updateInputWindowsLw將所有窗口的信息更新到IMS
2.窗口端連接的建立
當窗口端通過addwindow函數獲取InputChannel,便會使用它創建一個InputEventReceiver對象,可以接收來自InputChannel的輸入事件,觸發onInputEvent回調
InputEventRecevier如何工作?將InputChannel的可讀事件注冊到Looper,然后在事件到來時從InputChannel中讀取InputMessage,并翻譯成InputEvent,然后回調InputEventReceiver的onInputEvent
3.InputDispatcher與窗口的連接
事件的發送
派發循環是指InputDispatcher不斷地派發隊列取出事件,尋找合適的窗口并進行發送的過程,是InputDispatcher線程的主要工作
事件發送循環是InputDispatcher通過Connection對象將事件發送給窗口,并接受其反饋的過程
InputDispatcher->dispatchEventLocked:dispatchEventLocked根據InputTarget中的InputChannel找到對應的Connection
->InputDispatcher:prepareDispatcCycleLocked
->InputDispatcher:enqueueDispatchEntriesLocked
->InputDispatcher:startDispatchCycleLocked
輸入事件被InputPublisher以InputMessage的形式寫入InputChannel,然后將事件轉存到waitQueue中等待窗口的反饋
事件的接收
當InputPublisher將事件以InputMessage的形式寫入InputChannel中,窗口端的Looper會因此而被喚醒,并執行NativeInputEventReceiver的handleEvent調用consumeEvent
->android_view_InputEventReceiver->NativeInputEventReceiver:consumeEvent讀取一個InputEvent,生成java層的InputEvent對象,最后通過JNI回調
->InputEventReceier:dispatchInputEvent
事件的反饋與發送
->inputEventReceiver:finishInputEvent
->android_view_InputEventReceiver->NativeInputEventReceiver:finishInputEvent觸發服務端InputChannel回調
->InputDispatcher:handleReceiveCallback
->InputDispatcher:doDispatchCycleFinishedLockedInterruptible
對于輸入事件反饋的處理主要有兩個方面
將事件從Connection的waitQueue隊列中刪除,這個刪除動作標志著此事件的派發流程完成
最后調用startDispatchCycleLocked繼續嘗試發送隊列中的下一個事件
輸入事件ANR的產生
->InputDispatcher:findFocusedWindowTargetsLocked
1.窗口可以接收事件的條件
->InputDispatcher:isWindowReadyForMoreInputLocked判斷窗口是否可以接收事件:InputPublisher是否被阻塞以及Connection兩個隊列的狀態
2.重試派發與ANR的引發
->InputDispatcher:handleTargetsNotReadyLocked