当前位置: 首页 > news >正文

Android7 Input(五)InputDispatcher

概述

本文主要描述了Android Input框架中的InputDispatcher的功能和数据处理流程。InputDispatcher的功能总结成一句话就是处理InputReader传递过来的事件并将事件进行再次封装为一个InputDispatcher事件然后传递给App进行处理,当App处理完成后,通知InputDispatcher模块清理InputDispatcher上报的事件,即完成整个输入事件的处理流程。

本文涉及的源码路径

frameworks/native/services/inputflinger/InputDispatcher.cpp

frameworks/native/services/inputflinger/InputReader.cpp

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

InputDispatcher的初始化

InputDispatcher的初始化由InputManager完成,如下所示:

InputManager::InputManager(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {/* 创建事件分发对象 */mDispatcher = new InputDispatcher(dispatcherPolicy);/* 创建事件获取对象 */mReader = new InputReader(eventHub, readerPolicy, mDispatcher);initialize();
}

InputDispatcher初始化中传递了一个dispatcherPolicy参数,该参数是NativeInputManager对象,如下所示:

NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :mLooper(looper), mInteractive(true) {JNIEnv* env = jniEnv();......mInputManager = new InputManager(eventHub, this, this);
}

然后,我们进入InputDispatcher类的构造函数,如下所示:

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :mPolicy(policy),mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),mNextUnblockedEvent(NULL),mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {// 创建LoopermLooper = new Looper(false);mKeyRepeatState.lastKeyEntry = NULL;policy->getDispatcherConfiguration(&mConfig);
}

初始化过程比较简单,最关键就是创建了一个自己的Looper对象处理自己的消息,感兴趣的同学可以研究一下Looper的实现,这里我们不再详细描述其作用。

InputDispatcher的运行

InputDispatcher的运行在InputDispatcherThread线程中,如下所示:

void InputManager::initialize() {....../* 创建事件分发线程 */mDispatcherThread = new InputDispatcherThread(mDispatcher);
}status_t InputManager::start() {/* 开启事件分发线程 */status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);......return OK;
}

调用线程运行回调接口,如下所示:

 void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockAutoMutex _l(mLock);mDispatcherIsAliveCondition.broadcast();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.// 如果Command队列为空的情况下,才处理mInboundQueue队列中的事件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.// 处理command队列if (runCommandsLockedInterruptible()) {nextWakeupTime = LONG_LONG_MIN;}} // release lock// Wait for callback or timeout or wake.  (make sure we round up, not down)nsecs_t currentTime = now();// 计算epoll阻塞等待的超时时间int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);// 阻塞等待timeoutMillismLooper->pollOnce(timeoutMillis);
}

InputDispatcher的线程回调逻辑比较简单,主要逻辑如下:

1、优先处理Command队列中的事件,InputDispatcher将所有处理的事务,封装为一个Command事项加入Command队列等待处理。实现了事件的异步处理,并不需要同步等待处理结果。

2、当Command队列为空时,从InputDispatcher的事件管理队列mInboundQueue中获取事件(本质上是InputReader上报的事件)进行处理,处理的过程后面的章节有详细描述;

3、如果mInboundQueue队列也为空,则InputDispacher进入阻塞等待事件的到来;

InputDispatcher事件分发流程

当InputDispatcher开始运行时,Command队列为空,mInboundQueue队列也为空,InputDispatcher进入阻塞等待事件的到来。我们在前一个章节讲述过,InputReader最终将事件传递给InputDispatcher的notify*接口,比如触摸屏幕调用noifyMotion接口,如下所示:

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {....needWake = enqueueInboundEventLocked(newEntry);....if (needWake) {mLooper->wake();}
}

这个函数的接口很简单,将触摸事件加入到mInboundQueue队列调用mLooper-wake()唤醒InputDispatcher线程继续执行,最终进入dispatchOnceInnerLocked接口,如下所示:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();....if (!mPendingEvent) {// 如果分发事件队列为空if (mInboundQueue.isEmpty()) {....} else {// Inbound queue has at least one entry.// 从mInboundQueue队列中取出一个待分发事件mPendingEvent = mInboundQueue.dequeueAtHead();traceInboundQueueLengthLocked();}// Poke user activity for this event.if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {pokeUserActivityLocked(mPendingEvent);}// Get ready to dispatch the event.// 复位anr时间resetANRTimeoutsLocked();}......switch (mPendingEvent->type) {......case EventEntry::TYPE_MOTION: {MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {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 = dispatchMotionLocked(currentTime, typedEntry,&dropReason, nextWakeupTime);break;}default:ALOG_ASSERT(false);break;}// 分发完成,进行清理工作if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately}
}

dispatchOnceInnerLocked接口的逻辑如下:

1、从mInboundQueue队列中取出一个待处理的事件mPendingEvent;

2、重置ANR时间;

3、根据事件的类型,调用相应的接口进行处理,对于触摸屏设备来说,调用dispatchMotionLocked()进行处理;

4、事件处理完成后, 释放entry;

我们主要讲述触摸屏事件的处理,因此进入dispatchMotionLocked()接口,进一步进行分析

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {// Preprocessing.......bool conflictingPointerActions = false;int32_t injectionResult;if (isPointerEvent) {// Pointer event.  (eg. touchscreen)// 发现目标窗口injectionResult = findTouchedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime, &conflictingPointerActions);} else {// Non touch event.  (eg. trackball)injectionResult = findFocusedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime);}if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {return false;}......// 开始向App分发dispatchEventLocked(currentTime, entry, inputTargets);return true;
}

dispatchMotionLocked接口的逻辑主要如下:

1、找到事件分发的目标窗口,也就是响应事件的焦点窗口,关于目标窗口的寻找这里不再展开描述。

2、找到目标窗口后,执行dispatchEventLocked()开始向App进程发送事件,实现如下:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {......for (size_t i = 0; i < inputTargets.size(); i++) {const InputTarget& inputTarget = inputTargets.itemAt(i);// 根据inputTarget.inputChannel找到连接目标ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);if (connectionIndex >= 0) {// 找到链接通道sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);// 开始向连接通道分发事件prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);} else {
#if DEBUG_FOCUSALOGD("Dropping event delivery to target with channel '%s' because it ""is no longer registered with the input dispatcher.",inputTarget.inputChannel->getName().string());
#endif}}
}

dispatchEventLocked的逻辑如下:

1、找到与App连接的InputChannel通道(本质上是Unix 域socket套接字);

2、调用prepareDispatchCycleLocked接口,开始分发;

prepareDispatchCycleLocked的接口, 我们直接进入核心实现如下:

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {......// Not splitting.  Enqueue dispatch entries for the event as is.enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

enqueueDispatchEntriesLocked的实现:

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {bool wasEmpty = connection->outboundQueue.isEmpty();// Enqueue dispatch entries for the requested modes.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);// If the outbound queue was previously empty, start the dispatch cycle going.// connection连接输出通道队列不为空,开始向App分发事件if (wasEmpty && !connection->outboundQueue.isEmpty()) {startDispatchCycleLocked(currentTime, connection);}
}

这个接口,多次调用enqueueDispatchEntryLocked(),为了便于阅读,我们只保留了核心代码,该接口的实现如下所示:

void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,int32_t dispatchMode) {......// 重新构造一个DispatchEntry分发实体DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments refinputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,inputTarget->scaleFactor);......// Remember that we are waiting for this dispatch to complete.if (dispatchEntry->hasForegroundTarget()) {incrementPendingForegroundDispatchesLocked(eventEntry);}// Enqueue the dispatch entry.// 将dispatchEntry加入到连接通道的输出队列的队尾connection->outboundQueue.enqueueAtTail(dispatchEntry);traceOutboundQueueLengthLocked(connection);
}

enqueueDispatchEntryLocked接口的主要核心逻辑就是将eventEntry事件封装为一个dispatchEntry实体,然后加入到连接通道的输出队列;

当dispatchEntry事件被加入到connect的输出队列后,我返回到enqueueDispatchEntriesLocked接口的尾部,调用startDispatchCycleLocked()正式向App进程发送输入事件:

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {.......// 连接通道正常,并且输出队列不为空while (connection->status == Connection::STATUS_NORMAL&& !connection->outboundQueue.isEmpty()) {// 分发EntryDispatchEntry* dispatchEntry = connection->outboundQueue.head;// 设置开始分发的时间dispatchEntry->deliveryTime = currentTime;// Publish the event.status_t status;EventEntry* eventEntry = dispatchEntry->eventEntry;switch (eventEntry->type) {case EventEntry::TYPE_MOTION: {MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);......// Publish the motion event.// 分发触摸事件status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,motionEntry->deviceId, motionEntry->source,dispatchEntry->resolvedAction, motionEntry->actionButton,dispatchEntry->resolvedFlags, motionEntry->edgeFlags,motionEntry->metaState, motionEntry->buttonState,xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,motionEntry->downTime, motionEntry->eventTime,motionEntry->pointerCount, motionEntry->pointerProperties,usingCoords);break;}default:ALOG_ASSERT(false);return;}......}// Re-enqueue the event on the wait queue.// 将dispatchEntry移动到等待队列connection->outboundQueue.dequeue(dispatchEntry);traceOutboundQueueLengthLocked(connection);connection->waitQueue.enqueueAtTail(dispatchEntry);traceWaitQueueLengthLocked(connection);}
}

startDispatchCycleLocked()接口的实现:

1、 主要处理加入到connect输出队列中的dispachEntry,然后根据事件的类型,调用各自的处理接口,对于触摸屏幕来说,就是调用publishMotionEvent,如下所示:

status_t InputPublisher::publishMotionEvent(......InputMessage msg;msg.header.type = InputMessage::TYPE_MOTION;msg.body.motion.seq = seq;msg.body.motion.deviceId = deviceId;msg.body.motion.source = source;msg.body.motion.action = action;msg.body.motion.actionButton = actionButton;msg.body.motion.flags = flags;msg.body.motion.edgeFlags = edgeFlags;msg.body.motion.metaState = metaState;msg.body.motion.buttonState = buttonState;msg.body.motion.xOffset = xOffset;msg.body.motion.yOffset = yOffset;msg.body.motion.xPrecision = xPrecision;msg.body.motion.yPrecision = yPrecision;msg.body.motion.downTime = downTime;msg.body.motion.eventTime = eventTime;msg.body.motion.pointerCount = pointerCount;for (uint32_t i = 0; i < pointerCount; i++) {msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);}// 向unix socket写入msg, 这样App进程就能夸进程可以读到数据return mChannel->sendMessage(&msg);
}

重新再次创建一个App可识别的消息msg,然后调用mChannel->sendMessage进行发送,关于InputChannel,我们下一篇文章进行讲述其实现原理(本质上是向unix socket发送数据);

2、发送完事件后,将dispatchEntry事件从输出队列移动到waitQueue队列;

至此,我们讲述完了InputDispatcher向App目标窗口分发事件的整个数据处理的流程。

InputDispatcher响应App发送的信息

当App处理完InputDispatcher上报的事件后,App通过输入通道InputChannel,告诉InputDispatcher处理结果,最终回调handleReceiveCallback()进行事件的后期处理,handleReceiveCallback在进行输入通道注册时,加入到InputDispatcher的looper监控中,实现如下:

// 注册输入通道
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {......// 将输入通道的文件描述符加入到mLooper监控// 当向输入通道写入数据时,回调handleReceiveCallback()接口mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);} // release lock// Wake the looper because some connections have changed.mLooper->wake();return OK;
}

我们下一篇会讲解InputDispatcher与App进行交互的桥梁InputChannel的实现,这里不展开描述,我们只关注App处理完事件后,会向输入通道InputChannel写入信息,将事件的后期处理交给InputDispatcher进行处理,最后会回调handleReceiveCallback接口,如下所示:

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {InputDispatcher* d = static_cast<InputDispatcher*>(data);{ // acquire lockAutoMutex _l(d->mLock);......sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);......for (;;) {uint32_t seq;bool handled;status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);if (status) {break;}d->finishDispatchCycleLocked(currentTime, connection, seq, handled);gotOne = true;}if (gotOne) {d->runCommandsLockedInterruptible();if (status == WOULD_BLOCK) {return 1;}}notify = status != DEAD_OBJECT || !connection->monitor;if (notify) {ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",connection->getInputChannelName(), status);}} else {// Monitor channels are never explicitly unregistered.// We do it automatically when the remote endpoint is closed so don't warn// about them.notify = !connection->monitor;if (notify) {ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  ""events=0x%x", connection->getInputChannelName(), events);}}// Unregister the channel.d->unregisterInputChannelLocked(connection->inputChannel, notify);return 0; // remove the callback} // release lock
}

1、调用receiveFinishedSignal从App哪里获取数据,核心实现如下所示:

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {......InputMessage msg;// 从输入通道获取msgstatus_t result = mChannel->receiveMessage(&msg);if (result) {*outSeq = 0;*outHandled = false;return result;}......*outSeq = msg.body.finished.seq;*outHandled = msg.body.finished.handled;return OK;
}

2、调用finishDispatchCycleLocked,改接口最终调用onDispatchCycleFinishedLocked()进行事件的清理,核心实现如下所示:

void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {// 构造一个结束分发的commandCommandEntry* commandEntry = postCommandLocked(& InputDispatcher::doDispatchCycleFinishedLockedInterruptible);commandEntry->connection = connection;commandEntry->eventTime = currentTime;commandEntry->seq = seq;commandEntry->handled = handled;
}

构造一个Command. 并加入到InputDispatcher的mCommandQueue队列中,

3、当接受完所有的App发送的数据后,跳出死循环,进入runCommandsLockedInterruptible进行处理,实现如下:

bool InputDispatcher::runCommandsLockedInterruptible() {if (mCommandQueue.isEmpty()) {return false;}do {CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();Command command = commandEntry->command;(this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'commandEntry->connection.clear();delete commandEntry;} while (! mCommandQueue.isEmpty());return true;
}

该函数的实现非常简单,处理mCommandQueue队列中的command,然后调用command注册的回调。对于App下发的信息处理回调接口是doDispatchCycleFinishedLockedInterruptible,实现如下:

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);if (dispatchEntry) {bool restartEvent;if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);restartEvent = afterKeyEventLockedInterruptible(connection,dispatchEntry, keyEntry, handled);} else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);restartEvent = afterMotionEventLockedInterruptible(connection,dispatchEntry, motionEntry, handled);} else {restartEvent = false;}// Dequeue the event and start the next cycle.// Note that because the lock might have been released, it is possible that the// contents of the wait queue to have been drained, so we need to double-check// a few things.if (dispatchEntry == connection->findWaitQueueEntry(seq)) {// 从waitQueue队列中删除connection->waitQueue.dequeue(dispatchEntry);// 是否需要事件重发if (restartEvent && connection->status == Connection::STATUS_NORMAL) {connection->outboundQueue.enqueueAtHead(dispatchEntry);traceOutboundQueueLengthLocked(connection);} else {// 释放dispatchEntryreleaseDispatchEntryLocked(dispatchEntry);}}// Start the next dispatch cycle for this connection.// 继续分发startDispatchCycleLocked(now(), connection);}
}

doDispatchCycleFinishedLockedInterruptible()接口的主要逻辑就是:

1、从waitQueue队列中找到App处理完成的InputDispatcher,然后释放InputDispatcher占用的资源,标记该事件处理完毕;

2、检查是否需要重发事件,重发逻辑不展开描述;

3、继续事件分发;

至此,我们讲述完了App通知InputDispatcher进行事件的清理工作。

总结

本文主要讲述了InputDispatcher事件向App分发和App处理完事件后告知InputDispatcher的处理流程。下一篇讲述InputDispatcher和App进行交互的桥梁InputChannel的实现;

相关文章:

  • Missashe考研日记-day27
  • 碰一碰发视频源码搭建全解析,支持OEM
  • 分类数据处理全解析:从独热编码到高维特征优化
  • 如何解决docker运行Java程序导出Excel中文报错的问题?
  • [官方IP] Shift RAM
  • 五年经验Java开发如何破局创业
  • ShaderToy学习笔记 01.基础知识
  • 【WSL】wsl2出现Exec format error的解决办法
  • tensor 内部存储结构
  • FastAPI 零基础入门指南:10 分钟搭建高性能 API
  • 365打卡第R3周: RNN-心脏病预测
  • YOLOv5修改检测框颜色,粗细,标签大小,标签名称
  • AI编程案例拆解|基于机器学习XX评分系统-后端篇
  • 深入理解算力:从普通电脑到宏观计算世界
  • 【Docker项目实战】使用Docker部署Caddy+vaultwarden密码管理工具(详细教程)
  • 如何在项目中使用双token机制?
  • 代码随想录算法训练营Day36
  • MyBatis XML 配置完整示例(含所有核心配置项)
  • 单片机-89C51部分:4、固件烧录
  • MAVLink协议:原理、应用与实践
  • 文旅部:推动离境退税购物便利化有利于更多国内优质商品走出去
  • 持续更新丨伊朗官员:港口爆炸事件已致5人死亡
  • 魔都眼·上海车展⑥|周六客流超13.5万人次,创开展新高
  • 第二十届华表奖提名名单公布,张译、王一博、马丽、郭帆等入围
  • 三大猪企去年净利润同比均较大幅度增长,资产负债率齐降
  • 最高法知识产权法庭:6年来新收涉外案件年均增长23.2%