Android 输入事件拦截机制

Keyboard产生按键事件后,会通过notifyKey开始传递:

cpp 复制代码
frameworks\native\services\inputflinger\InputDispatcher.cpp
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

        mLock.lock();



        if (shouldSendKeyToInputFilterLocked(args)) {

            mLock.unlock();



            policyFlags |= POLICY_FLAG_FILTERED;

            if (!mPolicy->filterInputEvent(&event, policyFlags)) {

                return;  如果event被InputFilter消费掉了,直接返回,结束Input事件分发流程

            }



            mLock.lock();

        }



        int32_t repeatCount = 0;

        //将处理后的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



    if (needWake) {

        mLooper->wake();

    }

}

上面的代码有两个比较重要, 一个是interceptKeyBeforeQueueing, 另一个filterInputEvent,filterInputEvent被调用的前提是shouldSendKeyToInputFilterLocked,也就是说Java端的IMS通过nativeSetInputFilterEnabled设置了InputFilter, 即在Java层做Input filter动作,所以如果Java层filterInputEvent即消费了Input事件,此时Input分发事件就结束掉.

interceptKeyBeforeQueueing这个intercept是在将input Event enqueue到InputDispatcher之前做的拦截.

cpp 复制代码
frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
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;

        }

    }
}

interceptKeyBeforeQueueing在native层基本上没做什么, 只是call Java层也就是IMS的interceptKeyBeforeQueueing, 然后将拦截结果传递给 handleInterceptActions

cpp 复制代码
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

    }

}

如果interceptKeyBeforeQueueing拦截结果为1的话,在JAVA层对应的是ACTION_PASS_TO_USER, 意思是拦截的结果是没有设置该bit, 即表明JAVA层IMS消费了该事件。如果返回值为负数,那么result=-1,入则,返回值等于0则不处理,因为result默认初始化值为0,如果返回值大于0则把milliseconds_to_nanoseconds的返回值给result。但是特别注意,这里并没有结束Input事件传递。 而是将policy保存到input event里,继续分发流程。

InputDispatcher在有event事件发生后,会触发dispatchOnceInnerLocked

cpp 复制代码
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;

    }



    if (mNextUnblockedEvent == mPendingEvent) {

        mNextUnblockedEvent = NULL;

    }



    switch (mPendingEvent->type) {

    ...



    case EventEntry::TYPE_KEY: {

        KeyEntry* typedEntry = static_cast<KeyEntry*>(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) {

       //进入清理工作,最终调用synthesizeCancelationEventsForAllConnectionsLocked向所有的

       //input client端发送cancel事件,即一个ACTION_UP事件, keycode还是被拦截的keycode

       dropInboundEventLocked(mPendingEvent, dropReason);

        }

        mLastDropReason = dropReason;



        releasePendingEventLocked();

        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately

    }
}

在dispatchKeyLocked里结束了事件分发, 最后由dropInboundEventLocked向所有的input client发送 cancel 的事件,即一个ACTION_UP事件,还是被拦截的keycode.

如果interceptKeyBeforeQueueing没有拦截成功,那么就该轮着interceptKeyBeforeDispatching

cpp 复制代码
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,

     ...

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

        }

    }

...

}

input event第一次进来interceptKeyResult默认为INTERCEPT_KEY_RESULT_UNKNOWN, 而且interceptKeyBeforeDispatching并没有拦截,所以entry->policyFlags&POLICY_FLAG_PASS_TO_USER=true 这没什么好说的, 如上代码所示,dispatchKeyLocked函数在post一个command后直接返回了,并没有继续往下发送输入事件了。postCommandLocked将待执行的函数指针保存到mCommandQueue队列中。

cpp 复制代码
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()) { //检查mCommandQueue队列是否为空,

            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()) {//执行 mCommandQueue 里的command函数

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

}

InputDispatcher::dispatchOnce函数会先检查 mCommandQueue中队列是否为空,如果不为空会优先执行mCommandQueue里的函数,所以此时就开始执行

doInterceptKeyBeforeDispatchingLockedInterruptible

cpp 复制代码
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();

}

doInterceptKeyBeforeDispatchingLockedInterruptible调用Java层的interceptKeyBeforeDispatching做拦截操作,然后根据返回结果设置 key event的interceptKeyResult, 如果没有拦截,设置interceptKeyResult为INTERCEPT_KEY_RESULT_CONTINUE, 否则设置为INTERCEPT_KEY_RESULT_SKIP或TRY_AGAIN.

当runCommandsLockedInterruptible返回为true时, 会设置nextWakeupTime,进而设置timeoutMillis, 然后looper的pollOnce会立即timeout, 然后会再执行一次 dispatchOnce,dispatchOnceInnerLocked中处理的mPendingEvent正是传给JAVA层进行拦截操作的Event.,然后将mPendingEvent传递给dispatchKeyLocked,

为什么mPendingEvent还是原来的那个KeyEvent呢?因为

postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible) 后 return false.

相关推荐
二流小码农1 小时前
鸿蒙开发:信息标记组件
android·ios·harmonyos
利维亚的杰洛特1 小时前
【Android15 ShellTransitions】(九)结束动画+Android原生ANR问题分析
android
二流小码农2 小时前
鸿蒙开发:单一手势实现多次点击事件
android·ios·harmonyos
江上清风山间明月2 小时前
一周掌握Flutter开发--8. 调试与性能优化(下)
android·flutter·性能优化
Mr_万能胶3 小时前
要失业了!写在 Android “不再开源”之后
android·android studio·android jetpack
Renounce4 小时前
【Android】ViewModel和AndroidViewModel区别
android
珹洺4 小时前
Java-servlet(九)前端会话,会话管理与Cookie和HttpSession全解析
android·java·服务器·开发语言·前端·数据库·servlet
七郎的小院4 小时前
性能优化ANR系列之-Service ANR原理
android·客户端
百锦再4 小时前
全方位分析Vue与React:现代前端框架深度比较
android·前端·javascript·vue.js·react.js·前端框架
pengyu5 小时前
系统化掌握Dart异步编程(七):Isolate筑基篇
android·flutter·dart