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.youkuaiyun.com/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事件类似,不赘述了