我们在AndroidR Input子系统(3)InputReader线程
中分析了EventHub
读取了输入系统的原始事件之后会将其转换为NotifyKeyArgs
(对于按键事件来说),接着会调用InputDispatcher
的notifyKey
函数将NotifyKeyArgs
发送到InputDispatcher
线程做进一步处理。
我们先来简单看看NotifyKeyArgs
这个结构体,它是在KeyboardInputMapper
的processKey
函数中构造的:
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
......
NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
......
}
其构造函数如下:
// --- NotifyKeyArgs ---
NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime)
: NotifyArgs(id, eventTime), //eventTime:事件发生的时间
deviceId(deviceId), //事件发生的设备ID
source(source), //事件的来源,可以是物理键盘,游戏手柄等
displayId(displayId), //事件发生的显示器ID
policyFlags(policyFlags), //事件的flag,指示该事件是否有特殊含义,如唤醒设备等
action(action), //事件是按下还是抬起
flags(flags), //此参数为AKEY_EVENT_FLAG_FROM_SYSTEM,用来确认事件来自真实用户的点击
keyCode(keyCode), //Android键盘码
scanCode(scanCode), //系统扫描码
metaState(metaState),
downTime(downTime) {} //记录时间按下的时间,对于down事件其实就等于eventTime
系统发生的一个事件使用NotifyKeyArgs
来描述,这个结构体包含了事件的所有信息,将它传递给了InputDispatcher
。
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
//对事件有效性进行简单判断
if (!validateKeyEvent(args->action)) {
return;
}
//将NotifyKeyArgs中的事件信息解析出来
uint32_t policyFlags = args->policyFlags;
int32_t flags = args->flags;
int32_t metaState = args->metaState;
constexpr int32_t repeatCount = 0;
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
if (policyFlags & POLICY_FLAG_FUNCTION) {
metaState |= AMETA_FUNCTION_ON;
}
//表示当前事件可信任,添加特定flag
policyFlags |= POLICY_FLAG_TRUSTED;
int32_t keyCode = args->keyCode;
//构造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);
android::base::Timer t;
//mPolicy指向NativeInputManager,这个函数最终会通过JNI调到java层
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
}
//是否需要唤醒InputDispatcher线程
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();
}
//构造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);
//将事件加入队列
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
这个函数会将NotifyKeyArgs
封装的事件信息解析出来,构造两个结构体KeyEvent
和KeyEntry
,为什么是两个?因为KeyEvent
是会首先传递到java层的PhoneWindowManager
中去判断是否将此事件加入分发队列,我们就来看看这其中的流程。
mPolicy
指向NativeInputManager
:
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
ATRACE_CALL();
//mInteractive初始默认值为true,这个值用来表示当前是否处于用户交互状态
//可通过函数setInteractive修改
bool interactive = mInteractive.load();
if (interactive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
//InputDispatcher的notifyKey函数中已经将policyFlags添加了POLICY_FLAG_TRUSTED
//所以这里为true
if ((policyFlags & POLICY_FLAG_TRUSTED)) {
nsecs_t when = keyEvent->getEventTime();
JNIEnv* env = jniEnv();
//将native层keyEvent转换为java层keyEvent
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
jint wmActions;
if (keyEventObj) {
//mServiceObj指向java层InputManagerService,这里调用其interceptKeyBeforeQueueing方法
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;
}
}
}
这个函数中有两个重点,1.通过函数android_view_KeyEvent_fromNative
将native层keyEvent
转换为java层的keyEvent
,2.通过InputManagerService
的interceptKeyBeforeQueueing
的方法将keyEvent
传递到java层。
我们先来看keyEvent
的转换:
jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
jobject eventObj =
env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
event->getId(),
nanoseconds_to_milliseconds(event->getDownTime()),
nanoseconds_to_milliseconds(event->getEventTime()),
event->getAction(), event->getKeyCode(),
event->getRepeatCount(), event->getMetaState(),
event->getDeviceId(), event->getScanCode(),
event->getFlags(), event->getSource(),
event->getDisplayId(), hmac.get(), nullptr);
if (env->ExceptionCheck()) {
ALOGE("An exception occurred while obtaining a key event.");
LOGE_EX(env);
env->ExceptionClear();
return NULL;
}
return eventObj;
}
转换比较简单,直接解析native层KeyEvent
,构造java层的KeyEvent
,gKeyEventClassInfo.clazz
指向android.view.KeyEvent.java这个类,gKeyEventClassInfo.obtain
指向这个类的构造方法。
接着java层KeyEvent
构造好了之后会调用InputManagerService
的interceptKeyBeforeQueueing
方法:
// Native callback.
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
这里就直接调到PhoneWindowManager
中去了,PhoneWindowManager
中对这些事件的处理就很复杂了,不是本篇重点,跳过。
最后事件到java层处理之后会拿到返回值wmActions
来判断此次事件是否加入分发队列,正常wmActions
返回1的情况则将policyFlags
添加上POLICY_FLAG_PASS_TO_USER
的标志表面此事件需要继续分发。
接着我们继续回到InputDispatcher::notifyKey函数:
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
......
bool needWake;
{ // acquire lock
mLock.lock();
//事件是否过滤,由mInputFilterEnabled决定,默认值为false,可由setInputFilterEnabled修改
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
//调到java层InputManagerService的filterInputEvent方法,如果设置了事件过滤器
//就会结束分发
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
mLock.lock();
}
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);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
//唤醒InputDispatcher线程
mLooper->wake();
}
}
函数后半部分会将构造的KeyEntry
加入mInboundQueue
队列,并判断是否需要唤醒InputDispatcher线程来处理事件:
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
//如果mInboundQueue队列为空则一定会唤醒InputDispatcher线程
bool needWake = mInboundQueue.empty();
//事件放入队列尾部
mInboundQueue.push_back(entry);
traceInboundQueueLengthLocked();
//事件类型
switch (entry->type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
//这里主要是对APP切换事件的判断,包含HOME,ENDCALL,APP_SWITCH三种事件
if (isAppSwitchKeyEvent(keyEntry)) {
if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
//当满足条件时,增加按键0.5s的超时时间,超时之后其他按键事件会丢弃
mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
.......
}
return needWake;
}
mInboundQueue
是一个存储EventEntry
的队列,EventEntry
是各种类型事件的顶级父类,除了Key类型事件KeyEntry
,还有如下这些子类:
MotionEntry,DeviceResetEntry,ConfigurationChangedEntry。
上面这个函数将KeyEntry
保存到了mInboundQueue
尾部,并对几个特殊按键HOME,ENDCALL,APP_SWITCH增加了0.5s超时时间,超时之后会丢弃其他按键事件。
notifyKey
函数最后将会根据情况唤醒InputDispatcher线程,我们先回顾一下AndroidR Input子系统(2)Input子系统的启动
中分析的InputDispatcher线程启动后会创建自己线程的Looper对象并调用dispatchOnce
函数,来看看这个函数的具体实现:
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
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;
}
// If we are still waiting for ack on some events,
// we might have to wake up earlier to check if an app is anr'ing.
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
//进入等待
mLooper->pollOnce(timeoutMillis);
}
这个函数关键点就是调用了Looper的pollOnce
函数,熟悉Looper,handler机制的同学应该很清楚这个函数的作用,它会让当前线程陷入死循环处理事件,核心原理采用epoll
机制对指定fd进行监听,对于InputDispatcher线程来说,主要监听了两个fd,一个是Looper创建时就会默认添加到监听的mWakeEventFd
,另一个是后面要分析的InputChannel
机制的fd。
了解epoll
机制的都应该知道epoll
主要作用是对目标fd进行监听,当fd上没有事件发生时则会陷入等待状态,mWakeEventFd
的作用就是唤醒epoll
,具体如何唤醒的呢?就是调用Looper的wake
函数:
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
我们可以看到Looper的wake
函数实现很简单,就是向mWakeEventFd
中写入了一个int 1,此时Looper就被唤醒了,即InputDispatcher线程被唤醒了,唤醒之后首先判断haveCommandsLocked
函数返回false即mCommandQueue
为空才会执行dispatchOnceInnerLocked
。
bool InputDispatcher::haveCommandsLocked() const {
return !mCommandQueue.empty();
}
mCommandQueue
是一个存储CommandEntry
结构体的队列,CommandEntry
是用来描述一条指令,什么指令,调度当前input事件的指令,构造CommandEntry
时需要向其构造函数传递一个Command
对象,这个Command
其实是个函数指针:
typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
struct CommandEntry {
explicit CommandEntry(Command command);
......
}
如果mCommandQueue
不为空会是什么情况?不为空就会调用runCommandsLockedInterruptible
函数执行mCommandQueue
中的指令:
bool InputDispatcher::runCommandsLockedInterruptible() {
if (mCommandQueue.empty()) {
return false;
}
do {
std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
mCommandQueue.pop_front();
Command command = commandEntry->command;
command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
commandEntry->connection.clear();
} while (!mCommandQueue.empty());
return true;
}
执行指令即循环取出队列中所有CommandEntry
,并执行其command
函数,command
函数就是构造CommandEntry
时传入的函数指针,至于CommandEntry
什么时候构造的,如何构造的我们接着分析代码。
接着mCommandQueue
队列为空就会调用dispatchOnceInnerLocked
函数:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
//当前时间
nsecs_t currentTime = now();
//mDispatchEnabled默认为false
if (!mDispatchEnabled) {
//重置上一个事件
resetKeyRepeatLocked();
}
// 分发被冻结,WMS冻屏时会设置这个值,例如转屏
if (mDispatchFrozen) {
if (DEBUG_FOCUS) {
ALOGD("Dispatch frozen. Waiting some more.");
}
return;
}
//对于特殊按键HOME,END_CALL,APP_SWITCH增加了0.5s超时时间,
//超时之后会丢弃其他即将处理的按键事件。即isAppSwitchDue为true的情况
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
...
//如果待处理的事件mPendingEvent为空
if (!mPendingEvent) {
//mInboundQueue队列为空,表明没有任何事件
if (mInboundQueue.empty()) {
....
} else {
//否则取出mInboundQueue队头事件
mPendingEvent = mInboundQueue.front();
//并从队列移除
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
//这里将会调到java层PowerManagerService中去
pokeUserActivityLocked(*mPendingEvent);
}
}
bool done = false;
//初始化事件丢弃原因
DropReason dropReason = DropReason::NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
//当前事件不包含POLICY_FLAG_PASS_TO_USER的标志,则不合法,需要丢弃
dropReason = DropReason::POLICY;
} else if (!mDispatchEnabled) {
//当前事件被禁止分发,需要丢弃
dropReason = DropReason::DISABLED;
}
...
switch (mPendingEvent->type) {
......
//对于按键类型事件的处理分支
case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*typedEntry)) {
//重置mAppSwitchDueTime
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
//由于APP切换需要丢弃非HOME,ENDCALL,APP_SWITCH之外的事件
dropReason = DropReason::APP_SWITCH;
}
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
//当前处理事件的时间减去事件产生的时间大于10s也需要丢弃
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
//对按键类型事件来说不会走到这,mNextUnblockedEvent默认值为null
dropReason = DropReason::BLOCKED;
}
//进一步分发按键事件
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
......
}
//事件处理完成
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
//最后事件还是被丢弃
dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
//将事件release掉
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
上面函数中如果事件最后没有丢弃则会调用dispatchKeyLocked
进一步处理:
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
//对按键重复性的一些细节处理,不关注,省略
......
// interceptKeyResult用来描述当前按键的执行结果,
//初始化默认值为INTERCEPT_KEY_RESULT_UNKNOWN
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
//延迟处理当前按键,interceptKeyWakeupTime延迟处理的时间
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,command函数为doInterceptKeyBeforeDispatchingLockedInterruptible
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
//focusedWindowHandle 用来描述当前获得焦点的窗口信息
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
if (focusedWindowHandle != nullptr) {
//这里将当前焦点窗口的inputChannel保存到commandEntry中,
//inputChannel是一个很重要的东西,它是用来唤醒目标窗口处理事件的
commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
}
//将当前按键KeyEntry保存到CommandEntry
commandEntry->keyEntry = entry;
//将CommandEntry加入Command队列,待执行
postCommandLocked(std::move(commandEntry));
entry->refCount += 1;
//直接返回,等待Command队列执行
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) {
//interceptKeyResult 为INTERCEPT_KEY_RESULT_SKIP,会丢弃该事件
if (*dropReason == DropReason::NOT_DROPPED) {
*dropReason = DropReason::POLICY;
}
}
if (*dropReason != DropReason::NOT_DROPPED) {
//该按键被丢弃
setInjectionResult(entry,
*dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
: INPUT_EVENT_INJECTION_FAILED);
//丢弃之后的回调
mReporter->reportDroppedKey(entry->id);
return true;
}
std::vector<InputTarget> inputTargets;
//寻找合法焦点窗口
int32_t injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
//分发按键
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
dispatchKeyLocked
函数中对于输入事件,并不是直接拿到就处理,而是首先为其构造CommandEntry
,并添加到mCommandQueue
队列,然后此函数就返回了,返回的false表明此事件并未处理完成。
而我们前面有说过mCommandQueue
这个队列,它会在runCommandsLockedInterruptible
函数中被遍历出来执行其内部Command
函数,构造CommandEntry
时传入的函数指针为doInterceptKeyBeforeDispatchingLockedInterruptible
,接下来会先调用这个函数判断按键事件是否提前拦截:
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
//将KeyEntry转换为KeyEvent
KeyEntry* entry = commandEntry->keyEntry;
KeyEvent event = createKeyEvent(*entry);
mLock.unlock();
android::base::Timer t;
sp<IBinder> token = commandEntry->inputChannel != nullptr
? commandEntry->inputChannel->getConnectionToken()
: nullptr;
//这里会通过JNI调到java层PhoneWindowManager中去提前处理按键
nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
}
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();
}
上面函数会将按键在发给应用窗口之前先将按键发给java层PhoneWindowManager
的interceptKeyBeforeDispatching
方法,这就给了我们提前拦截按键的机会,像如果有需求禁用HOME
,RECENT
,BACK
就可以到interceptKeyBeforeDispatching
方法中对应case中返回-1就行了。
最后此函数有三个结果,1. 完全拦截按键,2. 按键立即执行,3. 按键延时执行。
接着按键的提前拦截执行完之后,再次执行到dispatchKeyLocked
函数之时就可以根据PhoneWindowManager
提前拦截的结果进行下一步处理了,如果entry->interceptKeyResult
不等于INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
和INTERCEPT_KEY_RESULT_SKIP
就会接着调用findFocusedWindowTargetsLocked
函数寻找按键分发的焦点窗口:
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
std::string reason;
int32_t displayId = getTargetDisplayId(entry);
//首先找到焦点窗口,关于焦点窗口如何得来的,以后有时间单独分析
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
//找到焦点应用程序,由java层InputMonitor传下来
sp<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
//没有焦点窗口,没有焦点应用
//按键事件分发状态为失败
return INPUT_EVENT_INJECTION_FAILED;
}
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
//没有焦点窗口,有焦点应用
//这种情况可能导致ANR,mNoFocusedWindowTimeoutTime用来对ANR timeout进行计时
if (!mNoFocusedWindowTimeoutTime.has_value()) {
//获取ANR计时timeout,默认值为5s
const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
//ANR的timeout时间为:按键在InputDispatch线程开始分发的时间 + 默认值(5s)
mNoFocusedWindowTimeoutTime = currentTime + timeout;
//可能发生ANR的应用
mAwaitedFocusedApplication = focusedApplicationHandle;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
//记录ANR的时间
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
//按键事件分发状态为等待
return INPUT_EVENT_INJECTION_PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
//当前时间大于ANR的timeout,则说明已经发生ANR了
ALOGE("Dropping %s event because there is no focused window",
EventEntry::typeToString(entry.type));
//按键事件状态为失败
return INPUT_EVENT_INJECTION_FAILED;
} else {
//按键事件分发状态为等待
return INPUT_EVENT_INJECTION_PENDING;
}
}
//走到这里说明当前按键事件可以有效分发个窗口了,
//此函数会将ANR timeout和可能发生ANR的应用程序清空
resetNoFocusedWindowTimeoutLocked();
// 权限检查
......
if (focusedWindowHandle->getInfo()->paused) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
return INPUT_EVENT_INJECTION_PENDING;
}
//对于按键类型事件,需要等之前的输入事件全部处理完成之后再进行,因为之前的事件可能
//导致焦点窗口的变化。
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
return INPUT_EVENT_INJECTION_PENDING;
}
}
// 按键事件到这里就能成功发给目标窗口了
addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
BitSet32(0), inputTargets);
//按键事件分发状态为成功
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
findFocusedWindowTargetsLocked
函数的主要作用是对按键事件是否能够成功分发的条件判断,我们总结一下此函数:
mFocusedWindowHandlesByDisplay
和mFocusedApplicationHandlesByDisplay
获得当前的焦点窗口和焦点应用,提一点,焦点窗口和焦点应用并不一定是一样,从java层来说焦点窗口指的是Winodw
,焦点应用指的是ActivityRecord
,一般对于系统类型窗口以及子窗口来说焦点窗口和焦点应用就不一样,而对于Activity
和Dialog
则一致。dispatchOnceInnerLocked
开始计时 + 5s,超过这个时间输入事件还没处理完则会上报ANR。addWindowTargetLocked
函数将接收按键事件的目标窗口的一些信息保存到inputTargets
集合中。我们简单看看addWindowTargetLocked
函数:
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
//查找inputTargets集合中是否已经包含了目标焦点窗口的InputTarget
std::vector<InputTarget>::iterator it =
std::find_if(inputTargets.begin(), inputTargets.end(),
[&windowHandle](const InputTarget& inputTarget) {
return inputTarget.inputChannel->getConnectionToken() ==
windowHandle->getToken();
});
const InputWindowInfo* windowInfo = windowHandle->getInfo();
//没有包含
if (it == inputTargets.end()) {
InputTarget inputTarget;
//获取目标窗口的inputChannel
sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
return;
}
//构造inputTarget
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
//放入inputTargets
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
windowInfo->windowXScale, windowInfo->windowYScale);
}
代码也很简单,首先inputTargets
容器中没有目标焦点窗口的inputTarget
就会以目标焦点窗口的信息构造一个,最重要的信息就是目标焦点窗口的inputChannel
。
接着再回到dispatchKeyLocked
函数,最终就会调用dispatchEventLocked
函数继续对输入事件进行分发:
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
//调到java层PowerManagerService
pokeUserActivityLocked(*eventEntry);
//遍历inputTargets
for (const InputTarget& inputTarget : inputTargets) {
//获取目标窗口的连接
sp<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
//执行prepareDispatchCycleLocked
prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
//异常情况
.....
}
}
}
这里会遍历inputTargets
,并调用prepareDispatchCycleLocked
函数,这里有个很重要的参数connection
,它是用来描述窗口的连接,每一个窗口在创建时都会创建一个连接,这个连接就是用来将输入事件传递到目标窗口,后面我会单独写一篇分析窗口的connection
。
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
const InputTarget& inputTarget) {
...
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::STATUS_NORMAL) {
//connection状态不正常,return
...
return;
}
//目标焦点窗口处于分屏模式
if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
//跳过
......
}
// 非分屏情况
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
const InputTarget& inputTarget) {
.....
//目标窗口的connection的outboundQueue是否为空
bool wasEmpty = connection->outboundQueue.empty();
// 调用了六次enqueueDispatchEntryLocked函数,仅仅最后一个参数不同
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
//如果目标窗口的connection的outboundQueue在执行enqueueDispatchEntryLocked之前为空
//并且在执行完enqueueDispatchEntryLocked之后不为空
if (wasEmpty && !connection->outboundQueue.empty()) {
//这种情况才继续往下执行
startDispatchCycleLocked(currentTime, connection);
}
}
上面调用了六次enqueueDispatchEntryLocked
函数,并且只有最后一个参数InputTarget
的flag不同,我们来看看这个函数的作用:
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
EventEntry* eventEntry,
const InputTarget& inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget.flags;
if (!(inputTargetFlags & dispatchMode)) {
//异常
return;
}
//这个规则目前不清楚
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
//将EventEntry转换为DispatchEntry
std::unique_ptr<DispatchEntry> dispatchEntry =
createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
// 再重新从DispatchEntry中获取EventEntry
EventEntry* newEntry = dispatchEntry->eventEntry;
//输入事件类型
switch (newEntry->type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
//简单将KeyEntry的一些信息解析出来存入dispatchEntry
dispatchEntry->resolvedEventId = keyEntry.id;
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
return; // skip the inconsistent event
}
break;
}
//其他类型,暂不关注
......
}
...
// 最后将dispatchEntry加入connection的outboundQueue队列
connection->outboundQueue.push_back(dispatchEntry.release());
}
对于按键类型的输入事件,此函数并不复杂,就是将KeyEntry
转换为了DispatchEntry
,最后加入目标窗口的connection
的outboundQueue
队列。
至于enqueueDispatchEntryLocked
函数多此调用,传入不同的InputTarget
flag只是标识当前输入事件的一些特性,在分析整体输入事件传递的流程上不必过多在意。
接着来看startDispatchCycleLocked
函数:
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
//目标窗口连接状态正常,并且连接的outboundQueue队列不为空
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
//取出队列头部DispatchEntry
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
const nsecs_t timeout =
getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
dispatchEntry->timeoutTime = currentTime + timeout;
// Publish the event.
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
//输入事件类型
switch (eventEntry->type) {
case EventEntry::Type::KEY: {
const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
// 调用publishKeyEvent函数分发按键
status =
connection->inputPublisher
.publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
keyEntry->deviceId, keyEntry->source,
keyEntry->displayId, std::move(hmac),
dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags, keyEntry->keyCode,
keyEntry->scanCode, keyEntry->metaState,
keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
//其他类型事件,暂不关注
......
}
// 分发结果
if (status) {
//分发失败
...
return;
}
//将处理完成的dispatchEntry从connection的outboundQueue移除
connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
connection->outboundQueue.end(),
dispatchEntry));
//将outboundQueue队列的dispatchEntry放入connection的waitQueue队列
connection->waitQueue.push_back(dispatchEntry);
...
}
}
startDispatchCycleLocked
会将目标窗口connection
的outboundQueue
队列中的dispatchEntry
依次取出来,对于按键类型的输入事件会调用connection->inputPublisher
的publishKeyEvent
进行分发,分发成功之后会将outboundQueue
队列中的dispatchEntry
移除,并转移到waitQueue
中,分发失败的情况我们就不去讨论了。
继续分析publishKeyEvent
函数:
status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action,
int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime) {
......
if (!seq) {
//异常情况
return BAD_VALUE;
}
//拿到输入事件的各种信息之后构造一个InputMessage
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
msg.body.key.seq = seq;
msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.displayId = displayId;
msg.body.key.hmac = std::move(hmac);
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
//通过InputChannel将InputMessage分发给目标窗口
return mChannel->sendMessage(&msg);
}
这个函数很简单,就是拿到startDispatchCycleLocked
传递的输入事件的各种信息,构造了一个InputMessage
,然后通过InputChannel将InputMessage
分发给目标窗口,我们会发现在分析InputDispatcher分发事件这一流程中反复看到InputChannel
,这时一个非常重要的点,后面我们单独分析。
我们再来看sendMessage
具体做了些什么:
status_t InputChannel::sendMessage(const InputMessage* msg) {
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
//异常情况,发送失败
return -error;
}
if (size_t(nWrite) != msgLength) {
return DEAD_OBJECT;
}
return OK;
}
sendMessage
的核心就是向mFd
写入数据,mFd
是什么呢?mFd
其实就是一对socket
的其中一个,InputChannel
在构造的时候是一对,对应了一对socket
,一个代表"client"
端,一个代表"server"
端,"server"
端被注册到了InputDispatcher,"client"
端返回给了APP进程,InputDispatcher和APP进程都会对自己的socket
一端进行监听,所以APP进程和InputDispatcher就这样完成了通信,后面的文章我们会看到其注册过程的。
到此文章就结束了,InputDispatcher对事件的分发流程是相当复杂的,我们仅对最简单的按键类型事件进行分析,同时分析过程省略了相当对细节的处理,我们做的是把握整体架构,现在就对这个过程做一个总结:
NotifyKeyArgs
传递给了InputDispatcher线程,在放入InputDispatcher线程mInboundQueue
队列之前会先将事件传递到java层PhoneWindowManager
,没有被拦截的情况下才会将NotifyKeyArgs
转换为KeyEntry
并放入mInboundQueue
队列,接着会唤醒InputDispatcher线程。dispatchOnceInnerLocked
处理事件,此函数在一些列判断之后没有丢弃事件则会进一步调用dispatchKeyLocked
函数。dispatchKeyLocked
函数在分发之前又会首先将按键事件传到java层PhoneWindowManager
的interceptKeyBeforeDispatching
中给个提前拦截的机会,如果没有被拦截则会通过findFocusedWindowTargetsLocked
找到目标焦点窗口。findFocusedWindowTargetsLocked
函数会从两个容器mFocusedWindowHandlesByDisplay
和mFocusedApplicationHandlesByDisplay
获得当前的焦点窗口和焦点应用,并且会对可能出现ANR的情况进行ANR timeout即ANR发生窗口的标记。findFocusedWindowTargetsLocked
函数返回结果为成功分发,则调用dispatchEventLocked
函数继续分发输入事件,接着会将KeyEntry
再转换为DispatchEntry
,并存入目标窗口连接connection
的outboundQueue
队列,然后调用publishKeyEvent
继续分发。publishKeyEvent
函数中构造描述输入事件信息的InputMessage
并通过InputChannel
向"server"
端socket
写入数据以唤醒APP进程的socket
"client"
端,自此输入事件成功从InputDispatcher发送到了APP。DispatchEntry
从目标窗口连接connection
的outboundQueue
队列中移除,并转移到目标窗口连接connection
的waitQueue
队列中。整个过程中有三个重要队列,mInboundQueue
,outboundQueue
,waitQueue
。
mInboundQueue
位于InputDispatcher线程,代表即将分发的输入事件,outboundQueue
位于目标窗口的connection
,代表即将要分发给目标窗口的输入事件,waitQueue
位于目标窗口的connection
,代表等待目标窗口处理的输入事件。