Android Input 4

这篇笔记主要记录Android Input的intercept, Fallback key, Joystick的方向键

先来一张overview

Keyevent.png

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, 那整个过程又是怎样的呢?

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, 具体流程如下

Joystick Direction keys

如图所示,当ViewPostImeInputStage也就是App并不consume该事件后,最后事件会被route到SyntheticInputStage中处理,然后根据 axie的值来转换为KEYCODE_DPAD_LEFT/RIGHT/UP/DOWN事件,接着将这些KeyEvent, 通过enqueueInputEvent,重新加入到looper里等待着执行。

你可能感兴趣的:(Android Input 4)