InputDispatcher拦截逻辑

InputDispatcher拦截事件
interceptKeyBeforeQueueing

应用为什么接收不到POWER按键的点击事件?
对于一些特殊按键,系统存在拦截策略,在InputDispatcher中是通过notifyKey将事件加到mInboundQueue中的,而拦截逻辑就是在这里进行的,重点是interceptKeyBeforeQueueing的逻辑,称之为第一次拦截逻辑

native/services/inputflinger/dispatcher/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ...
    //将NotifyKeyArgs转化成Java层的Keyevent对象
    KeyEvent event;
    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
                     args->downTime, args->eventTime);

    //调用policy的interceptKeyBeforeQueueing
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
     //将NotifyKeyArgs转化成KeyEntry对象
        KeyEntry* newEntry =
                new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
                             args->displayId, policyFlags, args->action, flags, keyCode,
                             args->scanCode, metaState, repeatCount, args->downTime);
    //把KeyEntry对象加到mInboundQueue中等待dispatchOnce方法处理
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    ...
}

这里面的mPolicy指的是NativeInputManager对象,对于方法如下

base/services/core/jni/com_android_server_input_InputManagerService.cpp

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
      ...
      //Native Callback调用Java层代码,wmActions为java层的返回值
      jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
      wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
      ...
      //处理wmActions的返回值
      handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
      ...
}

wmActions的值为通过Native Callback调用到Java层的返回值,具体是InputManagerService的interceptKeyBeforeQueueing方法的返回,实际就是调用到mWindowManagerCallbacks的相应方法,其中mWindowManagerCallbacks是在SystemServer的startOtherServices中传进来的,WindowManagerCallbacks中内部调用到mService.mPolicy中相应的方法

base/services/core/java/com/android/server/input/InputManagerService.java
 // Native callback.
    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
    }
    //mWindowManagerCallbacks对象在SystemServer.startOtherService中进行赋值
    public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
        mWindowManagerCallbacks = callbacks;
    }
    
   base/services/java/com/android/server/SystemServer.java
   //WMS的mian方法创建一个WMS对象,其中new PhoneWindowManager()传入一个新的PhoneWindowManager对象
    private void startOtherServices() {
            ...
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            
            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
            inputManager.start();
            ...
    }
    base/services/core/java/com/android/server/wm/WindowManagerService.java
    //内部类对象InputManagerCallback
    final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);
    public InputManagerCallback getInputManagerCallback() {
        return mInputManagerCallback;
    }
    
    final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
    //mService就是WMS对象
        public InputManagerCallback(WindowManagerService service) {
        mService = service;
    }
    //InputManagerCallback.interceptKeyBeforeQueueing就是调用到mService.mPolicy.interceptKeyBeforeQueueing方法
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
    }
    }
    
    //WMS构造方法对mPolicy赋值,其中policy就是PhoneWindowManager对象
     private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
            Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {

        mPolicy = policy;
        }
        base/services/core/java/com/android/server/policy/PhoneWindowManager.java
        //PhoneWindowManager实现WindowManagerPolicy接口
        public class PhoneWindowManager implements WindowManagerPolicy{
        }

如果我们进一步分析源码,会发现系统对于事件的拦截策略基本都是在PhoneWindowManager中实现的,
这里经过一系列的调用,wmAction的返回值其实就是PhoneWindowManager.interceptKeyBeforeQueueing的返回值,interceptKeyBeforeQueueing方法就是系统对一些按键事件进行处理的逻辑实现,而此方法逻辑中对Pweor按键是有特殊处理的,会返回一个~ACTION_PASS_TO_USER的Flag,系统中大部分需要系统进行处理的按键事件都在这里进行处理

base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    final int keyCode = event.getKeyCode();
    switch (keyCode) {
            case KeyEvent.KEYCODE_POWER: {
                //追加~ACTION_PASS_TO_USER flag,表示此按键不会传递给用户
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // 亮灭屏处理
                if (down) {
                    interceptPowerKeyDown(event, interactive);
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }
       return result;
}
base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
    /**
     * Pass this event to the user / app.  To be returned from
     * {@link #interceptKeyBeforeQueueing}.
     */
int ACTION_PASS_TO_USER = 0x00000001;

根据上面的注释,若Java层不拦截此按键事件,返回值为ACTION_PASS_TO_USER

//根据返回值设置flag
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    } 
}
//定义WM_ACTION_PASS_TO_USER值为1,同ACTION_PASS_TO_USER
enum {
    WM_ACTION_PASS_TO_USER = 1,
};

再看handleInterceptActions这个函数,若Java层返回ACTION_PASS_TO_USER==WM_ACTION_PASS_TO_USER,
Native层的policyFlags就或上POLICY_FLAG_PASS_TO_USER这个FLAG。并且保存在new KeyEntry对象中继续往下处理
policyFlags |= POLICY_FLAG_PASS_TO_USER
在InputDispatcher的分发方法dispatchOnceInnerLocked中根据policyFlags确定是否丢弃事件并写入dropReason,并在dispatchKeyLocked方法中实际进行丢弃清理工作

native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    //根据FLAG,确定dropReason是否丢弃此次事件
    DropReason dropReason = DropReason::NOT_DROPPED;    //默认不丢弃
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DropReason::POLICY;    //符合POLICY_FLAG_PASS_TO_USER,丢弃
    } else if (!mDispatchEnabled) {
        dropReason = DropReason::DISABLED;
    }
    switch (mPendingEvent->type) {
        case EventEntry::Type::KEY: {       //若是KEY事件,调用dispatchKeyLocked方法,参数包含dropReason
            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
            ...
            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }
    
    bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
        // 清理此需要丢弃事件
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(entry,
                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                             : INPUT_EVENT_INJECTION_FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;    //dropReason!=NOT_DROPPED 丢弃事件,直接返回,不调用dispatchEventLocked继续分发事件
    }
    
    dispatchEventLocked(currentTime, entry, inputTargets);
    ...
    }
}
interceptKeyBeforeDispatching

系统中拦截按键事件的地方不止这一出,还有filterInputEvent和interceptKeyBeforeDispatching

先来看filterInputEvent吧,这个被称为过滤的方法,
filterInputEvent被调用的前提是shouldSendKeyToInputFilterLocked,也就是说Java端的IMS通过nativeSetInputFilterEnabled设置了InputFilter, 即在Java层做Input filter动作,所以如果Java层filterInputEvent过滤了Input事件,此时Input分发事件就结束掉,但是这个只在AccessibilityManagerService中使用,其他基本用不到,在这里不关注InputFilter,我们下面来看interceptKeyBeforeDispatching,故名思义,这个intercept是在将input Event enqueue到InputDispatcher之后,分发之前做的拦截,我们可以称之为第二次拦截

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {

    // Give the policy a chance to intercept the key.
    //默认INTERCEPT_KEY_RESULT_UNKNOWN,第一次会执行到这里
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            sp<InputWindowHandle> focusedWindowHandle =
                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
            if (focusedWindowHandle != nullptr) {
                commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
            }
            commandEntry->keyEntry = entry;
            //建一个命令CommandEntry保存拦截方法,待下次runCommandsLockedInterruptible调用
            postCommandLocked(std::move(commandEntry));
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } 
}

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

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { 
    
    /* 命令队列为空时
        @a 从mInboundQueue中取出事件
        @b 用它来生成命令放入命令队列 or 直接丢弃
        @d 对于经过处理的事件:
          Global Key:丢弃
          System Key:丢弃
          User Key:找到target,执行dispatch*/
//blog.csdn.net/vviccc/article/details/91451184
        //检查mCommandQueue是否为空
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        
/*@c 当命令队列有数据时,执行命令:
Global Key:发广播
System Key:直接处理
User Key:不处理*/
        //执行mCommandQueue中的Command
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    ...
}

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

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

   //调用Java层的拦截逻辑,根据返回决定interceptKeyResult是否拦截
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &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();
}

这个方法中,会根据返回值给entry->interceptKeyResult变量赋值。从名字上我们可以猜测,返回值小于0则拦截事件,等于0则放行事件,大于0是待会再检测是否需要拦截

doInterceptKeyBeforeDispatchingLockedInterruptible通过Native Callback调用Java层的interceptKeyBeforeDispatching做拦截操作,逻辑同样都在PhoneWindowManager里实现,然后根据返回结果设置 key event的interceptKeyResult, 如果没有拦截,设置interceptKeyResult为INTERCEPT_KEY_RESULT_CONTINUE, 否则设置为INTERCEPT_KEY_RESULT_SKIP或TRY_AGAIN,即对应上面的<0,=0,>0

    @Override
    public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
            int policyFlags) {
        ... 
        if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
            if (mScreenshotChordVolumeDownKeyTriggered && !mScreenshotChordPowerKeyTriggered) {
                final long now = SystemClock.uptimeMillis();
                final long timeoutTime = mScreenshotChordVolumeDownKeyTime
                        + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
                if (now < timeoutTime) {
                    //Result>0,再执行一次拦截逻辑
                    return timeoutTime - now;
                }
            }
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mScreenshotChordVolumeDownKeyConsumed) {
                if (!down) {
                    mScreenshotChordVolumeDownKeyConsumed = false;
                }
                //Result<0,拦截事件,跳过接下来的分发
                return -1;
            }
        }
        ...
        // Let the application handle the key.
        //Result==0继续接下来的分发流程
        return 0;
    }

doInterceptKeyBeforeDispatchingLockedInterruptible只是设置KeyEvent的interceptKeyResult, 那这个key event何时才被处理呢??再回到 dispatchOnce方法

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { 
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
        int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
        mLooper->pollOnce(timeoutMillis);
}

当runCommandsLockedInterruptible返回为true时, 会设置nextWakeupTime,进而设置timeoutMillis, 然后looper的pollOnce会立即timeout, 然后会再执行一次 dispatchOnce,
此时在次进入dispatchOnceInnerLocked->dispatchKeyLocked

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

    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
    //根据command的返回值interceptKeyResult,第二次走到这里,若为INTERCEPT_KEY_RESULT_SKIP,上层拦截事件
        if (*dropReason == DropReason::NOT_DROPPED) {
            *dropReason = DropReason::POLICY;
        }
    }

    
    if (*dropReason != DropReason::NOT_DROPPED) {
        return true;
    }
}

整个拦截逻辑就是:
若Java层拦截这个事件,那么delay < 0, entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP,然后dropReason = DropReason::POLICY一样走Drop的逻辑

总结一下整体流程,如下
在这里插入图片描述

interceptMotionBeforeQueueingNonInteractive

当然除了Key事件,实际Motion事件也存在拦截逻辑,主要拦截实现是在PhoneWindowManager.interceptMotionBeforeQueueingNonInteractive中,但是只有一次拦截逻辑,逻辑和Key事件类似,不赘述了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值