按鍵事件在activity中的流程一文已經簡單介紹了按鍵事件在activity中的處理流程。本文則着重介紹事件進入activity之前的nativie和jni層的流程。
Native層的流程
native層相關類都在/frameworks/base/services/input目錄下,InputManager、InputReader、InputDispatcher、EventHub是幾個主要類。InputManager負責初始化的一些工作。在其構造方法裏,會創建InputReader、InputDispatcher、EventHub三個對象,並創建相應的reader和dispatcher線程。在InputManager的start()和stop()方法裏,會分別啓動和停止這2個線程。
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize(); // 初始化線程
}
InputReaderThread線程會循環調用InputReader的loopOnec()方法,而該方法會先從EventHub的getEvent()函數獲取到當前InputDevice的事件,然後在processEventsLocked()中處理這些事件,最終事件會傳遞到InputDevice的process()函數中。
void InputReader::loopOnce() {
// ... 一些本地變量
{ // acquire lock
// config change和timeout的一些邏輯
} // release lock
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); // 讀取事件並存入mEventBuffer
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) { // 事件處理
processEventsLocked(mEventBuffer, count);
}
// timeout和device change的一些邏輯
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
mQueuedListener->flush();
}
每一個InputDevice對應一種輸入設備,而每一種輸入設備都有自己的InputMapper列表。InputMapper的process()函數將RawEvent轉換成key、touch等事件。按鍵事件對應的是KeyboardInputMapper。在其process()函數中,會先底層按鍵根據其key map layout轉換爲java層定義的按鍵值,然後將處理過的事件轉換爲NotifyKeyArgs,並存入一個Vector,通知到InputListener的notifyKey()函數。而InputReader裏的InputListener正是在其構造方法裏傳入的InputDispatcher,即事件從InputReader裏傳遞到了InputDispatcher中。
下面再來看InputDispatcher的notifyKey()函數。
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
// 檢查key action是否有效...
// 設置policy flag和meta state ...
KeyEvent event;
// 初始化key event
event.initialize(args->deviceId, args->source, args->action,
flags, args->keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
// mPolicy是在InputDispatcher構造方法中傳入
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
if (policyFlags & POLICY_FLAG_WOKE_HERE) {
flags |= AKEY_EVENT_FLAG_WOKE_HERE;
}
bool needWake;
{ // acquire lock
mLock.lock();
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
mLock.lock();
}
int32_t repeatCount = 0;
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, args->keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
代碼中可以看到調用了mPolicy的interceptKeyBeforeQueueing()和filterInputEvent()函數,並將KeyEvent轉換爲了KeyEntry,存入事件隊列中。在InputDispatcherThread中,InputDispatcher的dispatchOnce()函數會被循環調用。而在dispatchOnce()函數中,會先將事件隊列中的事件一個個取出(第一個事件會先與PowerManagerService交互),找到當前焦點window作爲target,使用command模式,生成command並放入command隊列中,並在隨後的循環中,依次執行這些command。中間會調用到mPolicy的interceptKeyBeforeDispatching(),和InputDispatcher的dispatchEventLocked()函數。而事件將最終轉化爲DispatchEntry存入當前focus window對應的Connection的outboundQueue中,並在InputDispatcher的startDispatchCycleLocked()函數中依次被取出並通過inputPublisher的publishKeyEvent()函數分發出去。這個inputPublisher是在/frameworks/base/libs/androidfw/InputTransport.h中定義,而實現則在/frameworks/base/libs/androidfw/InputTransport.cpp中。在InputTransport.h中分別定義了InputChannel、InputPublisher、InputConsumer三個接口。InputPublisher作爲生產者,被InputDispatcher主動調用,將事件(key/touch)轉發給上層應用;InputConsumer作爲消費者,被上層應用主動調用,將事件(channel切換等)傳給InputDispatcher(抱歉,底層c++代碼看的並不是太明白,有錯誤請指出);而InputChannel使用管道做進程間通訊,提供上層和底層交互的機制。
這一段代碼邏輯太複雜,跨度太大,就不一一貼出來了,只說明主要的流程。
疑問:
InputDispatcher由InputManager初始化,而InputManager又是由誰初始化呢?InputChannel最終又將事件傳遞到了哪裏呢?
jni層的流程
jni層的代碼在/frameworks/base/services/jni中,主要是幾個以com_android_server_input_ 開頭的cpp文件。我們首先看com_android_server_input_InputManagerService.cpp。看名字就可以知道,它是InputManagerService.java類(輸入相關的系統服務)對應的jni接口。其核心類是NativeInputManager,而這個類實現了InputReaderPolicyInterface和InputDispatcherPolicyInterface接口。在NativeInputManager的構造方法中,就以自己爲參數直接創建了InputManager對象,並最終將自己做爲policy的實現傳遞給了InputReader和InputDispatcher。即上面最後一段代碼片段中看到的mPolicy就是這個NativeInputManager。NativeInputManager的interceptKeyBeforeQueueing()和filterInputEvent()都是簡單將native事件轉換爲java層的對應的事件對象,然後通過jni接口直接調用到了InputManagerService.java的同名函數中。另外在interceptKeyBeforeQueueing()函數中,還和PowerManagerService有一些簡單交互。
在上層的WindowManagerService中的addWindow()方法中,會調用InputChannel的openInputChannelPair()函數,最終這個InputChannel會保存在WindownState中。而java層的InputChannel和native的InputChannel是對應的。
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
// ....
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// ...
}
注意上邊的InputManagerService的registerInputChannel()函數。該方法會調用native層的com_android_server_input_InputManagerService.cpp中的同名方法,並最終調用到InputDispatcher的同名方法。而InputDispatcher在這裏會將InputChannel保存在Connection裏。在InputDispatcher的startDispatchCycleLocked()方法中的事件最終都會通過這個InputChannel傳到上層(抱歉,上層的InputEventReceiver如何和這個InputChannel對接並未弄明白)。最終在上層的InputEventReceiver的onInputEvent()函數中接收到這些事件。而InputEventReceiver的實現是在ViewRootImpl.java中,onInputEvent()回調中會調用ViewRootImpl的enqueueInputEvent()函數,最終進入PhoneWindow的內部類的dispatchKeyEvent()函數。如果事件未被處理,ViewRootImpl中會對方向鍵做查找焦點。如果不想使用默認的InputEventReceiver實現,還有一種方式是調用Window的takeInputQueue(InputQueue.Callback)函數,通過callback可以拿到所有的事件,自己做處理。細節邏輯可以查看ViewRootImpl的setView()函數。
疑問:
InputDispatcher的事件如何通過InputChannel傳給InputEventReceiver???
其它的細節
上邊已經提到了jni層的com_android_server_input_InputManagerService.cpp會調用java層InputManagerService的interceptKeyBeforeQueueing()、interceptKeyBeforeDispatching()、filterInputEvent()三個函數,下面我們再看看這3個函數的邏輯處理。其實在InputManagerService中還有很多其它jni函數,這些函數也都和native層InputManager的函數一一對應。
下面是這3個函數的代碼,很簡單的都傳給了回調。
// Native callback.
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(
event, policyFlags, isScreenOn);
}
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// Native callback.
final boolean filterInputEvent(InputEvent event, int policyFlags) {
synchronized (mInputFilterLock) {
if (mInputFilter != null) {
try {
mInputFilter.filterInputEvent(event, policyFlags);
} catch (RemoteException e) {
/* ignore */
}
return false;
}
}
event.recycle();
return true;
}
前2個函數中的mWindowManagerCallbacks是在SystemServer將WindowManagerService的InputMonitor傳遞給了InputManagerService,而這個InputMonitor又將這些事件傳給了WindowManagerService中的WindowManagerPolicy,而這個WindowManagerPolicy的最終實現在/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。在PhoneWindowManager中,interceptKeyBeforeQueueing()函數主要處理power、call事件(這些事件優先級最高,直接關係到screen on/off和電話)相關一些邏輯,如果要實現一些自定義按鍵開關screen的操作,可以放在這裏處理;而interceptKeyBeforeDispatching()則主要處理了home、search、tab事件,這些事件都會直接啓動相應的一些界面,如果要實現一些自定義按鍵啓動應用的操作,可以放在這裏處理。
第3個函數中的InputFilter則是在AccessbilityManagerService中通過WindowManagerService設置到InputManagerService中的,是一個AccessbilityInputFilter實例。這些事件會由系統中的AccessbilityService做處理,之後再有InputFilterHost送回InputManagerService,再由InputManagerService通過jni接口nativeInjectInputEvent()送回給native層的InputDispatcher,並最終在InputDispatcher的enqueueInboundEventLocked()函數中進入事件隊列。接下來對這些filted的事件和其它事件就是一樣的處理流程了。