这篇笔记主要记录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(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(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里等待着执行。