本文首發於 劉望舒的博客
地址:liuwangshu.cn/framework/i…html
關聯繫列
解析WMS系列
深刻理解JNI系列
輸入系統系列java
在上一篇文章中,咱們學習了IMS的誕生(建立),IMS建立後還會進行啓動,這篇文章咱們來學習IMS的啓動過程和輸入事件的處理。android
IMS的建立在SystemServer的startOtherServices方法中,不瞭解請查看Android輸入系統(一)輸入事件傳遞流程和InputManagerService的誕生這篇文章。 frameworks/base/services/java/com/android/server/SystemServer.java數組
private void startOtherServices() {
...
traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context);
traceEnd();
...
traceBeginAndSlog("StartInputManager");
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
traceEnd();
}
複製代碼
建立IMS後就會緊接着執行IMS的啓動。IMS的start方法以下所示。 frameworks/base/services/core/java/com/android/server/input/InputManagerService.java緩存
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}
複製代碼
IMS的start方法中,會將自身添加到Watchdog中進行監控,用於定時檢測系統關鍵服務(AMS和WMS等)是否可能發生死鎖。 nativeStart方法對應的JNI層的函數是什麼呢?查看com_android_server_input_InputManagerService的gInputManagerMethods數組,不理解JNI的能夠查看深刻理解JNI系列文章。 frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cppapp
static const JNINativeMethod gInputManagerMethods[] = {
...
{ "nativeStart", "(J)V",
(void*) nativeStart },
...
}
複製代碼
nativeStart方法對應的JNI函數爲nativeStart: frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp函數
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();//1
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
複製代碼
用reinterpret_cast操做符將jlong類型的ptr強制轉換爲原類型(NativeInputManager指針類型)。註釋1處會調用InputManager的start函數。 frameworks/native/services/inputflinger/InputManager.cppoop
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
複製代碼
能夠看到InputManager的start函數運行了InputReaderThread和InputDispatcherThread,這兩個線程在Android輸入系統(一)輸入事件傳遞流程和InputManagerService的誕生提到過,它們在InputManager的構造函數中被建立,其中InputReaderThread中運行了InputReader, InputDispatcherThread中運行了InputDispatcher。學習
先來回顧下InputDispatcher和InputReader是在哪建立的,InputManager的構造函數以下所示。 frameworks/native/services/inputflinger/InputManager.cppui
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();
}
複製代碼
能夠看到InputDispatcher和InputReader是有關聯的,InputDispatcher會做爲一個參數傳入到InputReader中。 InputDispatcher是在InputReader以前建立的,這個順序不能改變,由於要確保InputReader將加工後的輸入事件交給InputDispatcher時,InputDispatcher已經被建立。 InputDispatcher的定義以下所示。 frameworks/native/services/inputflinger/InputDispatcher.h
class InputDispatcherThread : public Thread {
public:
explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
~InputDispatcherThread();
private:
virtual bool threadLoop();
sp<InputDispatcherInterface> mDispatcher;
};
}
複製代碼
InputDispatcher.h中定義了threadLoop純虛函數,InputDispatcher繼承了Thread。native的Thread內部有一個循環,當線程運行時,會調用threadLoop函數,若是它返回true而且沒有調用requestExit函數,就會接着循環調用threadLoop函數。 查看InputDispatcherThread的threadLoop函數是如何實現的。 frameworks/native/services/inputflinger/InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
複製代碼
threadLoop函數中只調用了InputDispatcher的dispatchOnce函數: frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
if (!haveCommandsLocked()) {//1
dispatchOnceInnerLocked(&nextWakeupTime);//2
}
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
nsecs_t currentTime = now();//3
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);//4
mLooper->pollOnce(timeoutMillis);
}
複製代碼
註釋1處用於檢查InputDispatcher的緩存隊列中是否有等待處理 的命令,若是沒有就會執行註釋2處的dispatchOnceInnerLocked函數,用來將輸入事件分發給合適的Window。註釋3處獲取當前的時間,結合註釋4處,得出InputDispatcher須要睡眠的時間爲timeoutMillis。最後調用Looper的pollOnce函數使InputDispatcher進入睡眠狀態,並將它的最長的睡眠的時間設置爲timeoutMillis。當有輸入事件產生時,InputReader就會將睡眠狀態的InputDispatcher 喚醒,InputDispatcher會從新開始分發輸入事件。 那麼InputReader是如何喚醒InputDispatcher的呢? 咱們接着往下看。
InputReader是在InputReaderThread中啓動的,InputReaderThread和InputDispatcherThread的定義是相似的,也是繼承了Thread並定義了threadLoop純虛函數。若是處理的事件爲鍵盤輸入事件,則調用時序圖以下所示。
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
複製代碼
threadLoop函數中只調用了InputReader的loopOnce函數: frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::loopOnce() {
...
//經過EventHub的getEvents函數獲取事件信息存在mEventBuffer中
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//1
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
//若是有事件信息,調用processEventsLocked函數對事件進行加工處理
processEventsLocked(mEventBuffer, count);//2
}
...
}
複製代碼
註釋1處調用EventHub的getEvents函數來獲取設備節點的事件信息到mEventBuffer中,事件信息主要有兩種,一種是設備節點的增刪事件(設備事件),一種是原始輸入事件。註釋2處的processEventsLocked函數用於對mEventBuffer中的原始輸入事件信息進行加工處理,加工後的輸入事件會交由InputDispatcher來處理,processEventsLocked函數以下所示。 frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
//遍歷全部的事件
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
//事件類型分爲原始輸入事件和設備事件,這個條件語句對原始輸入事件進行處理
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
//處理deviceId所對應的設備的原始輸入事件
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//1
} else {
//對設備事件進行處理
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
複製代碼
InputReader的processEventsLocked函數首先遍歷了全部的事件,這些事件用RawEvent對象來表示,將原始 輸入事件和設備事件分開處理,其中設備事件分爲DEVICE_ADDED、DEVICE_REMOVED和FINISHED_DEVICE_SCAN,這些事件是在EventHub的getEvent函數中生成的。若是是DEVICE_ADDED事件(設備添加事件),InputReader會新建InputDevice對象,用來存儲設備信息,而且會將InputDevice存儲在 KeyedVector類型的容器mDevices中。 同一個設備的輸入事件交給processEventsForDeviceLocked函數來處理。 frameworks/native/services/inputflinger/InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);//1
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);//2
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
}
複製代碼
註釋1處根據deviceId從mDevices中獲取對應的deviceIndex,註釋2處再根據這個deviceIndex從mDevices中獲取對應的InputDevice。最後會調用InputDevice的process函數: frameworks/native/services/inputflinger/InputReader.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {*
size_t numMappers = mMappers.size();
//遍歷處理該InputDevice全部的事件
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
rawEvent->when);
#endif
//mDropUntilNextSync的值默認爲false,若是設備的輸入事件緩衝區溢出,這個值會置爲true。
if (mDropUntilNextSync) {
...
} else {
for (size_t i = 0; i < numMappers; i++) {//1
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);//2
}
}
}
}
複製代碼
首先會遍歷InputDevice中的全部的事件,真正加工原始輸入事件的是InputMapper對象,因爲原始輸入事件的類型不少,所以在InputMapper有不少子類,用於加工不一樣的原始輸入事件,好比KeyboardInputMapper用於處理鍵盤輸入事件,TouchInputMapper用於處理觸摸輸入事件。 註釋1處遍歷全部的InputMapper,在註釋2處將原始輸入事件交由這些InputMapper來處理,至因而哪一個InputMapper來處理,InputReader並不關心。 這裏就以處理鍵盤輸入事件爲例,KeyboardInputMapper的process函數以下所示。 frameworks/native/services/inputflinger/InputReader.cpp
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {//1
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);//2
}
break;
}
...
}
}
複製代碼
註釋1處,若是事件的類型爲按鍵類型的事件,就會調用註釋2處的KeyboardInputMapper的processKey函數。 frameworks/native/services/inputflinger/InputReader.cpp
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
...
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);//1
}
複製代碼
processKey函數會將加工後的鍵盤輸入事件封裝爲NotifyKeyArgs,將NotifyKeyArgs通知給InputListenerInterface。 InputDispatcher繼承了InputDispatcherInterface,而InputDispatcherInterface繼承了InputListenerInterface,所以註釋1處其實是調用了InputDispatcher的notifyKey函數,將NotifyKeyArgs交給InputDispatcher處理。 frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
...
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, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);//1
needWake = enqueueInboundEventLocked(newEntry);//2
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
複製代碼
代碼塊中採用Mutex互斥鎖的形式,在註釋1處根據NotifyKeyArgs,從新封裝一個KeyEntry對象,表明一次按鍵數據。註釋2處根據KeyEntry,來判斷是否須要將睡眠中的InputDispatcher喚醒,若是須要,就調用Looper的wake函數進行喚醒,InputDispatcher被喚醒後就會從新對輸入事件的分發,具體的回頭查看第2小節。
本文涉及到了四個關鍵的類,分別是IMS、EventHub、InputDispatcher和InputReader,它們作了以下的工做: