android N InputDispatcher中按键分发之notifyKey之后流程详解

  该篇文章仅分析notifyKey之后的流程,InputReader怎么读取之类的本文不关心.本文重点关注InputDispatcher和java层的交互,包括调用interceptKeyBeforeQueueing和interceptKeyBeforeDispatching方法,以及有辅助服务拦截按键时的处理.

1.notifyKey主函数

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
         ...
         ...
         ...
    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
        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);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

  主函数中有四个重要的调用,我们需要分别分析:

  1.mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);//最终会调用到PWM中对应的方法

  2.shouldSendKeyToInputFilterLocked(args)//和辅助服务相关

  3.needWake = enqueueInboundEventLocked(newEntry);//把按键加入队列

  4.mLooper->wake();//唤醒线程,从队列中去除按键事件执行分发

  首先分析第一步interceptKeyBeforeQueueing,mPolicy其实是NativeInputManager对象,去该对象中看看具体调用:

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    // Policy:
    // - Ignore untrusted events and pass them along.
    // - Ask the window manager what to do with normal events and trusted injected events.
    // - For normal events wake and brighten the screen if currently off or dim.
    bool interactive = mInteractive.load();
    if (interactive) {
        policyFlags |= POLICY_FLAG_INTERACTIVE;
    }
    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime();
        JNIEnv* env = jniEnv();
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }

        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
    } else {
        if (interactive) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;
        }
    }
}

  其中会主要是调用env->CallIntMethod(mServiceObj,gServiceClassInfo.interceptKeyBeforeQueueing,keyEventObj, policyFlags).该方法会调用InputManagerService的interceptKeyBeforeQueueing,InputManagerService又会调用PhoneWindowManager的interceptKeyBeforeQueueing,然后会有一个返回值赋给wmActions,而接下来handleInterceptActions会根据wmActions的值来更新policyFlags:

void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    } else {
#if DEBUG_INPUT_DISPATCHER_POLICY
        ALOGD("handleInterceptActions: Not passing key to user.");
#endif
    }
}

  也就意味着我们在PhoneWindowManager中可以通过返回值来控制policyFlags的值,事件分发过程中多次判断该值,决定要不要继续分发.

  第二步shouldSendKeyToInputFilterLocked,当有辅助服务配置接收按键的话,该方法返回true,进入判断,然后调用如下代码:

            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

  逻辑很简单,调用NativeInputManager的filterInputEvent方法,如果该返回值返回false则notifyKey方法直接返回,实际上只要shouldSendKeyToInputFilterLocked走进来的话,如果没有异常产生filterInputEvent总是返回false的,那么事件怎么分发传递的?这就是我们接下来要分析的:

bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
    // The callee is responsible for recycling the event.
    jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent,
            inputEventObj, policyFlags);
    if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
        pass = true;
    }
    env->DeleteLocalRef(inputEventObj);
    return pass;
}

  可以看出没有异常发生的话,返回的值为InputManagerService中返回的值:

    // 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;
    }

  在InputManagerService中,可以看到只要mInputFilter不为空就返回false,而上篇文章分析过了,如果系统存在接收按键的辅助服务,AccessibilityManagerService会调用setInputFilter,会更新mInputFilter,该值不会为空.所以可以得出结论,只要有接收按键的辅助服务,则notifyKey不会传递,但是,实际情况按键还是有效的,到底哪里把这些按键传递了呢?我们回到上篇文章分析过得mInputManager.setInputFilter(filter)方法里面去:

    public void setInputFilter(IInputFilter filter) {

            if (filter != null) {
                mInputFilter = filter;
                mInputFilterHost = new InputFilterHost();
                try {
                    filter.install(mInputFilterHost);
                } catch (RemoteException re) {
                    /* ignore */
                }
            }

            nativeSetInputFilterEnabled(mPtr, filter != null);
    }

  我们看见,有一个filter.install(mInputFilterHost)的操作,奥秘就在这里,InputFilter的filterInputEvent方法同时会调用到InputFilterHost的sendInputEvent方法,其中的调用读者可以自己去看,限于篇幅,不作分析,很简单.

  

        @Override
        public void sendInputEvent(InputEvent event, int policyFlags) {
            if (event == null) {
                throw new IllegalArgumentException("event must not be null");
            }

            synchronized (mInputFilterLock) {
                if (!mDisconnected) {
                    nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
                            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
                            policyFlags | WindowManagerPolicy.FLAG_FILTERED);
                }
            }
        }

  谜底已经揭开,nativeInjectInputEvent最终回调用InputDispatcher的injectInputEvent方法,该方法和notifyKey有相同的调用,就是我们上面分析的3,4步

  3.needWake = enqueueInboundEventLocked(newEntry);//把按键加入队列

  4.mLooper->wake();//唤醒线程,从队列中去除按键事件执行分发

  第三步比较简单,只是调用mInboundQueue.enqueueAtTail(entry),把事件放入队列.

  重点是第四步,相当复杂,开头就很复杂mLooper->wake()怎么就能唤醒线程了呢?这个我们要细细捋一捋了,里面牵扯到native的线程和looper的pollOnce,wake.

  SystemServer在启动的时候会去创建InputManagerService,在InputManagerService的构造函数中回调用nativeInit来初始化native层的InputManager,最终调用的的代码如下:  

InputManager::InputManager(
        const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

  其中创建了一个InputDispatcherThread对象,该对象父类为Thread,我们去看看Thread的源码

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast(user);

    sp strong(self->mHoldSelf);
    wp weak(strong);
    self->mHoldSelf.clear();

#if defined(__ANDROID__)
    // this is very useful for debugging with gdb
    self->mTid = gettid();
#endif

    bool first = true;

    do {
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) {
                // Binder threads (and maybe others) rely on threadLoop
                // running at least once after a successful ::readyToRun()
                // (unless, of course, the thread has already been asked to exit
                // at that point).
                // This is because threads are essentially used like this:
                //   (new ThreadSubclass())->run();
                // The caller therefore does not retain a strong reference to
                // the thread and the thread would simply disappear after the
                // successful ::readyToRun() call instead of entering the
                // threadLoop at least once.
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }

        // establish a scope for mLock
        {
        Mutex::Autolock _l(self->mLock);
        if (result == false || self->mExitPending) {
            self->mExitPending = true;
            self->mRunning = false;
            // clear thread ID so that requestExitAndWait() does not exit if
            // called by a new thread using the same thread ID as this one.
            self->mThread = thread_id_t(-1);
            // note that interested observers blocked in requestExitAndWait are
            // awoken by broadcast, but blocked on mLock until break exits scope
            self->mThreadExitedCondition.broadcast();
            break;
        }
        }

        // Release our strong reference, to let a chance to the thread
        // to die a peaceful death.
        strong.clear();
        // And immediately, re-acquire a strong reference for the next loop
        strong = weak.promote();
    } while(strong != 0);

    return 0;
}

   native层其实是调用pthread_create来创建线程的,把上述代码中的_threadLoop函数作为参数传递进去,那么实际会运行还函数,该函数会一直调用threadLoop函数,直到该函数返回值为false才执行完毕,而InputDispatcherThread的threadLoop方法返回值永远是true,那么该线程起来后会不断调用threadLoop方法

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

  其实会一直循环调用dispatchOnce方法,我们进入该方法看看:

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        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);
}

  该方法主要执行dispatchOnceInnerLocked,runCommandsLockedInterruptible方法这两个方法我们以后分析,还有会调用mLooper->pollOnce(timeoutMillis),该方法会使线程wait,wait的时间就是timeoutMillis,而timeoutMillis在一次按键被处理完成之后值其实是非常大的数字,可以理解为,该线程永远在wait的状态,而mLooper->wake()则可以让其退出wait的状态,综上所述我们可以简单描述下InputDispatcher分发时间的基本流程

  1.会有一个InputDispatcherThread线程一直循环读取事件队列中的事件,没有事件则会wait

  2.有事件触发的话则该事件会进入事件队列,并且会调用mLooper->wake()来唤醒InputDispatcherThread线程

  好了,现在我们第四步中的背景知识已经介绍完毕了,我们现在正式分析dispatchOnce方法,我们逐步分析,先分析

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            if (isAppSwitchDue) {
                // The inbound queue is empty so the app switch key we were waiting
                // for will never arrive.  Stop waiting for it.
                resetPendingAppSwitchLocked(false);
                isAppSwitchDue = false;
            }

            // Synthesize a key repeat if appropriate.
            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            // Nothing to do if there is no pending event.
            if (!mPendingEvent) {
                return;
            }
        } else {
            // Inbound queue has at least one entry.
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }

        // Get ready to dispatch the event.
        resetANRTimeoutsLocked();
    }

    // Now we have an event to dispatch.
    // All events are eventually dequeued and processed this way, even if we intend to drop them.
    ALOG_ASSERT(mPendingEvent != NULL);
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;
    } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
    }

    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = NULL;
    }

    switch (mPendingEvent->type) {
    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }

   
    default:
        ALOG_ASSERT(false);
        break;
    }

    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
    }
}

  首次进入时候mPendingEvent为空,则需要去队列中拿一个出来赋值给mPendingEvent,然后会调用dispatchKeyLocked方法并且把mPendingEvent作为参数传入,dispatchKeyLocked方法有一个返回值,如果为true的则会调用releasePendingEventLocked把mPendingEvent释放并置为空,去dispatchKeyLocked中看看

  

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Handle case where the policy asked us to try again later last time.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    // 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) {
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            if (mFocusedWindowHandle != NULL) {
                commandEntry->inputWindowHandle = mFocusedWindowHandle;
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        if (*dropReason == DROP_REASON_NOT_DROPPED) {
            *dropReason = DROP_REASON_POLICY;
        }
    }

    // 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);
        return true;
    }

    // Identify targets.
    Vector inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    addMonitoringTargetsLocked(inputTargets);

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

  可以看到主要逻辑为判断entry->interceptKeyResult的值,该值来决定进入哪个逻辑,interceptKeyResult的初始值为INTERCEPT_KEY_RESULT_UNKNOWN,我们先去看看该逻辑,该逻辑主要判断如果policyFlags中有POLICY_FLAG_PASS_TO_USER的话,则把该事件和doInterceptKeyBeforeDispatchingLockedInterruptible方法包装成一个commandEntry,放入command队列,并且该方法返回false.上段分析过如果返回为true才会去把mPendingEvent释放并置为空,则这个时候mPendingEvent还是不为空,dispatchOnceInnerLocked已经分析完了,我们接着走

        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }

  runCommandsLockedInterruptible中会去判断command队列是否为空,如果是空则返回false,刚才分析该队列不为空,则会执行那个command,并且返回true,其实会执行doInterceptKeyBeforeDispatchingLockedInterruptible方法:

  

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;

    KeyEvent event;
    initializeKeyEvent(&event, entry);

    mLock.unlock();

    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);

    mLock.lock();

    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();
}

  该方法最终会调用PWM的interceptKeyBeforeDispatching方法,然后根据返回值来更新interceptKeyResult,这个地方就是java层的第二次拦截了,我们完全可以返回负数,从而导致interceptKeyResult为INTERCEPT_KEY_RESULT_SKIP,从而跳过该事件.

  我们回到runCommandsLockedInterruptible中,该方法返回true,则nextWakeupTime = LONG_LONG_MIN,那么mLooper->pollOnce(timeoutMillis)不会等待马上执行,又会回到threadLoop函数中,回到了我们最初分析的第四步的开头,不同的是此时mPendingEvent不为空并且mPendingEvent的interceptKeyResult也不为INTERCEPT_KEY_RESULT_UNKNOWN,进入真正的按键传递,最终会调用publishKeyEvent传递给应用,这些流程读者自己分析.

你可能感兴趣的:(android N InputDispatcher中按键分发之notifyKey之后流程详解)