這篇筆記主要記錄Android Input的intercept, Fallback key, Joystick的方向鍵
先來一張overview
1. interceptKeyBeforeQueueing
Keyboard產生按鍵事件后,會通過notifyKey開始傳遞,至于前面的流程就不在這里啰嗦了。
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
...
uint32_t policyFlags = args->policyFlags; //只關注policyFlags特別重要
...
policyFlags |= POLICY_FLAG_TRUSTED; //指明這個input事件是來自于trusted source
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
bool needWake;
{ // acquire lock
if (shouldSendKeyToInputFilterLocked(args)) {
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // 如果event被InputFilter消費掉了,直接返回,結束Input事件分發流程
}
}
//將處理后的policy 保存到event里
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
...
}
上面的代碼有兩個比較重要, 一個是interceptKeyBeforeQueueing, 另一個filterInputEvent
先來看filterInputEvent吧
filterInputEvent被調用的前提是shouldSendKeyToInputFilterLocked,也就是說Java端的IMS通過nativeSetInputFilterEnabled設置了InputFilter, 即在Java層做Input filter動作,所以如果Java層filterInputEvent即消費了Input事件,此時Input分發事件就結束掉.
在這里不深究InputFilter的情況
下面來看interceptKeyBeforeQueueing,故名思義,這個intercept是在將input Event enqueue到InputDispatcher之前做的攔截.
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
...
if ((policyFlags & POLICY_FLAG_TRUSTED)) {
...
if (keyEventObj) {
wmActions = env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeQueueing,
keyEventObj, policyFlags);
if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
wmActions = 0;
}
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
} else {
...
}
}
interceptKeyBeforeQueueing在native層基本上沒做什么, 只是call Java層也就是IMS的interceptKeyBeforeQueueing, 然后將攔截結果傳遞給 handleInterceptActions
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
uint32_t& policyFlags) {
if (wmActions & WM_ACTION_PASS_TO_USER) { //WM_ACTION_PASS_TO_USER=1
policyFlags |= POLICY_FLAG_PASS_TO_USER;
} else {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("handleInterceptActions: Not passing key to user.");
#endif
}
}
如果interceptKeyBeforeQueueing攔截結果為1的話,在JAVA層對應的是ACTION_PASS_TO_USER, 意思是攔截的結果是沒有設置該bit, 即表明JAVA層IMS消費了該事件。但是特別注意,這里并沒有結束Input事件傳遞。 而是將policy保存到input event里,繼續分發流程。
那何時處理呢?
InputDispatcher在有event事件發生后,會觸發dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
DropReason dropReason = DROP_REASON_NOT_DROPPED;
//mPendingEvent就是上面所說的按鍵事件
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY; //如果event被IMS消費了,此時在這里會設置dropReason
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
switch (mPendingEvent->type) {
...
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
...
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
//即使攔截了,也要調用dispatchKeyLocked. 多此一舉么???奇怪
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
}
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
//進入清理工作,最終調用synthesizeCancelationEventsForAllConnectionsLocked向所有的
//input client端發送cancel事件,即一個ACTION_UP事件, keycode還是被攔截的keycode
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
...
}
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
//默認是UNKNOWN
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { //由于被攔截了,這里不會再調用了
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindowHandle != NULL) {
commandEntry->inputWindowHandle = mFocusedWindowHandle;
}
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
}
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
//原來在這里結果input事件分發啊
return true;
}
...
}
原來是在dispatchKeyLocked里結束了事件分發, 繞了一大圈啊. 最后由dropInboundEventLocked向所有的input client發送 cancel 的事件,即一個ACTION_UP事件,還是被攔截的keycode.
好了,interceptKeyBeforeQueueing 在這里就結束了
2. interceptKeyBeforeDispatching
如果interceptKeyBeforeQueueing沒有攔截成功,那么就該輪著interceptKeyBeforeDispatching
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Give the policy a chance to intercept the key.
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { //這里是1沒有攔截
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindowHandle != NULL) {
commandEntry->inputWindowHandle = mFocusedWindowHandle;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
logOutboundKeyDetailsLocked("dispatchKey return 1 - ", entry);
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
} ...
input event第一次進來interceptKeyResult默認為INTERCEPT_KEY_RESULT_UNKNOWN, 而且interceptKeyBeforeDispatching并沒有攔截,所以entry->policyFlags&POLICY_FLAG_PASS_TO_USER=true
這沒什么好說的, 如上代碼所示,dispatchKeyLocked函數在post一個command后直接返回了,并沒有繼續往下發送輸入事件了。
postCommandLocked將待執行的函數指針保存到mCommandQueue隊列中。
那doInterceptKeyBeforeDispatchingLockedInterruptible什么時候被執行的呢?
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ ...
if (!haveCommandsLocked()) { //檢查mCommandQueue隊列是否為空,
dispatchOnceInnerLocked(&nextWakeupTime);
}
if (runCommandsLockedInterruptible()) { //執行 mCommandQueue 里的command函數
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
}
InputDispatcher::dispatchOnce函數會先檢查 mCommandQueue中隊列是否為空,如果不為空會優先執行mCommandQueue里的函數,所以此時就開始執行
doInterceptKeyBeforeDispatchingLockedInterruptible
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
...
nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
&event, entry->policyFlags);
if (delay < 0) {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
} else if (!delay) {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
entry->interceptKeyWakeupTime = now() + delay;
}
entry->release();
}
doInterceptKeyBeforeDispatchingLockedInterruptible調用Java層的interceptKeyBeforeDispatching做攔截操作,然后根據返回結果設置 key event的interceptKeyResult, 如果沒有攔截,設置interceptKeyResult為INTERCEPT_KEY_RESULT_CONTINUE, 否則設置為INTERCEPT_KEY_RESULT_SKIP或TRY_AGAIN.
doInterceptKeyBeforeDispatchingLockedInterruptible只是設置KeyEvent的interceptKeyResult, 那這個key event何時才被處理呢??
再回到 dispatchOnce
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
當runCommandsLockedInterruptible返回為true時, 會設置nextWakeupTime,進而設置timeoutMillis, 然后looper的pollOnce會立即timeout, 然后會再執行一次 dispatchOnce,
此時進入dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
if (! mPendingEvent) { //此時mPendingEvent就是上面傳遞給JAVA層執行攔截操作的event.
...
}
...
switch (mPendingEvent->type) {
...
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
...
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
//清空mPendingEvent
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
dispatchOnceInnerLocked中處理的mPendingEvent正是傳給JAVA層進行攔截操作的Event.,然后將mPendingEvent
傳遞給dispatchKeyLocked,
為什么mPendingEvent還是原來的那個KeyEvent呢?因為
postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible)
后 return false
.
下面就自己看了
3. Fallback Key事件
Scenario, 按鍵盤上的 ESC (退出)鍵, 正常的流程是 Input 系統往 App端發送 KEYCODE_ESCAPE
事件,通常情況下 App是不會處理這個按鍵的,接著Input又向App發送 KEYCODE_BACK
事件, KEYCODE_BACK
就是fallback的key, 那整個過程又是怎樣的呢?
如圖所示, 當App端在處理KEYCODE_ESCAPE
事件后(不管有沒有consume),都會往SystemServer端發送finish的消息,如果沒有被處理且有fallback的key,將fallback的key event enqueue, 最后通過startDispatcherCycleLocked來啟動下一次input事件dispatch.
圖中綠色的流程是運行于java端.
InputManagerService通過dispatchUnhandledKey往native獲得Fallback的KeyEvent. Fallback的key定義在 /system/usr/keychars
4. Joystick的方向按鍵
Joystick的方向鍵,并不是KeyEvent, 而是MotionEvent, 具體流程如下
如圖所示,當ViewPostImeInputStage也就是App并不consume該事件后,最后事件會被route到
SyntheticInputStage
中處理,然后根據 axie的值來轉換為KEYCODE_DPAD_LEFT/RIGHT/UP/DOWN事件,接著將這些KeyEvent, 通過enqueueInputEvent,重新加入到looper里等待著執行。