按鍵事件在native和jni中的流程
按鍵事件在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的事件和其它事件就是一樣的處理流程了。