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.

相关推荐
2501_9151063227 分钟前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
超低空33 分钟前
Android MediaSession深度解析:车载音乐播放器完整案例
android·架构·客户端
00后程序员张33 分钟前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
xiaoshiquan12061 小时前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157683 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户095 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户095 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户095 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º8 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
非专业程序员Ping15 小时前
HarfBuzz概览
android·ios·swift·font