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

frameworks 之InputDispatcher

frameworks 之InputDispatcher

  • 1. 填充Iq
  • 2.进入循环
  • 3.进入oq
  • 4. 发布消息,并将数据放进去wq
  • 5. 接收消息
  • 6. 移除wq

android 输入事件 主要分 2个流程 事件读取事件分发。本文讲解事件分发流程。
涉及到的类如下
-frameworks/native/services/inputflinger/InputClassifier.cpp
-frameworks/native/services/inputflinger/InputListener.cpp
-frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
-frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
-frameworks/native/libs/input/InputTransport.cpp
-frameworks/base/core/java/android/view/InputEventReceiver.java

上一张讲到 事件输入读取后 会通过 mQueuedListener->flush 进行事件分发的开端。该方法遍历对应的数组,并调用对应的notify。

1. 填充Iq

// frameworks/native/services/inputflinger/InputListener.cpp// 遍历 队列数组 调用 args的 notify, mInnerListener为 InputClassifier
// notify方法 对应的实现类 args的 notify 方法, 触摸事件对应的是 NotifyMotionArgs
void QueuedInputListener::flush() {size_t count = mArgsQueue.size();for (size_t i = 0; i < count; i++) {NotifyArgs* args = mArgsQueue[i];args->notify(mInnerListener);delete args;}mArgsQueue.clear();
}

放进数组是不同的实体类,所以调用notify对应不同的方法,触摸事件归属实体体为 NotifyMotionArgs,所以调用的是 notifyMotion 方法,而 listener 属于初始化传入的即对应的是 InputClassifier

// frameworks/native/services/inputflinger/InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {listener->notifyMotion(this);
}

查看 InputClassifier 对应的 notifiyMotion 函数,该函数对全局事件进行判断。如果不需要则直接调用,需要则进行拷贝新数据对全局事件进行通知。该类对应的mListener 也是初始化传入的,即为 InputDispatcher

// frameworks/native/services/inputflinger/InputClassifier.cpp
// 触摸事件刷新
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {std::scoped_lock lock(mLock);// MotionClassifier is only used for touch events, for now// 判断需要全局手势,不需要则直接返回,需要则拷贝一个新的const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);if (!sendToMotionClassifier) {mListener->notifyMotion(args);return;}NotifyMotionArgs newArgs(*args);newArgs.classification = mMotionClassifier->classify(newArgs);mListener->notifyMotion(&newArgs);
}

查看 InputDispatcher 的 notifyMotion 方法,会先对对应的事件添加可信任标记,并添加对应的信任。事件分发前会通过 interceptMotionBeforeQueueing,进行处理。mPolicy 为 com_android_server_input_InputManagerService ,该方法会调用对应的java进行屏幕亮屏。并构造对应的 MotionEntry 实体类,通过 enqueueInboundEventLocked 放进去iq队列。并调用wake 方法对 looper进行唤醒。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 对类型进行转化,然后通知looper唤醒进行处理
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {...// 判断是否符合的触摸事件if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,args->pointerProperties)) {return;}uint32_t policyFlags = args->policyFlags;// 添加受信任的标记policyFlags |= POLICY_FLAG_TRUSTED;android::base::Timer t;// 对放进去事件前进行处理mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",std::to_string(t.duration().count()).c_str());}bool needWake;{ // acquire lockmLock.lock();// 是否消费过滤全局事件, 这样就不继续消费if (shouldSendMotionToInputFilterLocked(args)) {mLock.unlock();MotionEvent event;ui::Transform transform;..policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy->filterInputEvent(&event, policyFlags)) {return; // event was consumed by the filter}mLock.lock();}// Just enqueue a new motion event.// 创建一个新的实体类 MotionEntry type 为 MOTIONstd::unique_ptr<MotionEntry> newEntry =std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,args->source, args->displayId, policyFlags,args->action, args->actionButton, args->flags,args->metaState, args->buttonState,args->classification, args->edgeFlags,args->xPrecision, args->yPrecision,args->xCursorPosition, args->yCursorPosition,args->downTime, args->pointerCount,args->pointerProperties, args->pointerCoords, 0, 0);// 找到窗口那些放进去 iq队列,并唤醒needWake = enqueueInboundEventLocked(std::move(newEntry));mLock.unlock();} // release lockif (needWake) {mLooper->wake();}
}

interceptMotionBeforeQueueing 方法 首先对是否可交互,即是否为亮屏,添加对应的flag, 然后通过jni,调用phoneWindowManager的回调interceptMotionBeforeQueueingNonInteractive对屏幕进行唤醒。

// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
// 当放进去iq队列前对屏幕进行检测
void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,uint32_t& policyFlags) {ATRACE_CALL();// Policy:// - Ignore untrusted events and pass them along.// - No special filtering for injected events required at this time.// - Filter normal events based on screen state.// - For normal events brighten (but do not wake) the screen if currently dim.bool interactive = mInteractive.load();if (interactive) {policyFlags |= POLICY_FLAG_INTERACTIVE;}if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {if (policyFlags & POLICY_FLAG_INTERACTIVE) {policyFlags |= POLICY_FLAG_PASS_TO_USER;} else {JNIEnv* env = jniEnv();// 调用phoneWindowManager的回调interceptMotionBeforeQueueingNonInteractive对屏幕进行唤醒jint wmActions = env->CallIntMethod(mServiceObj,gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,displayId, when, policyFlags);if (checkAndClearExceptionFromCallback(env,"interceptMotionBeforeQueueingNonInteractive")) {wmActions = 0;}handleInterceptActions(wmActions, when, /*byref*/ policyFlags);}} else {if (interactive) {policyFlags |= POLICY_FLAG_PASS_TO_USER;}}
}

将对应的数据放进去mInboundQueue,也叫 iq。然后在通过 shouldPruneInboundQueueLocked 检测是否找到对应的窗口,是的话返回true。也会检测是否有注册全局手势,有的也返回true。

// 将对应的数据放进 iq 队列等待读取
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {// 判断队列是否为空bool needWake = mInboundQueue.empty();// 放进去队尾mInboundQueue.push_back(std::move(newEntry));// 从队尾拿出EventEntry& entry = *(mInboundQueue.back());traceInboundQueueLengthLocked();switch (entry.type) {case EventEntry::Type::KEY: {// Optimize app switch latency.// If the application takes too long to catch up then we drop all events preceding// the app switch key.const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);if (isAppSwitchKeyEvent(keyEntry)) {if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {mAppSwitchSawKeyDown = true;} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCHALOGD("App switch is pending!");
#endifmAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;mAppSwitchSawKeyDown = false;needWake = true;}}}break;}case EventEntry::Type::MOTION: {// 判断是否对应的触摸窗口,找到的话 返回true,将标志位值置为trueif (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {mNextUnblockedEvent = mInboundQueue.back();needWake = true;}break;}case EventEntry::Type::FOCUS: {LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");break;}case EventEntry::Type::CONFIGURATION_CHANGED:case EventEntry::Type::DEVICE_RESET:case EventEntry::Type::SENSOR:case EventEntry::Type::POINTER_CAPTURE_CHANGED:case EventEntry::Type::DRAG: {// nothing to dobreak;}}// 返回true唤醒队列return needWake;
}

2.进入循环

looper唤醒后,会进入 dispatchOnce 方法。该方法会通过 haveCommandsLocked 判断是否有命令消息。(判断命令数组是否为空,触摸事件结束后,会通过finish事件放到)为空就进入分发。不为空表示有正在处理的事件进入runCommandsLockedInterruptible

// 开始循环
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.// 判断common命令是否为空,为空就进入分发。不为空表示有正在处理的事件进入runCommandsLockedInterruptibleif (!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 eventsif (nextWakeupTime == LONG_LONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // 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);
}

进入 dispatchOnceInnerLocked 方法。该方法先判断是否不可分发,不可分发则重置变量并置为空拦截。 如果是 mDispatchFrozen 冻结状态,则返回但不会对变量重置。然后判断是否有需要进行处理的事件,如果没有的话就从iq队列获取并赋值给 mPendingEvent。接下来判断事件类型,触摸事件类型为 Type::MOTION, 会判断事件丢失的原因并调用 dispatchMotionLocked 进行事件处理。

// 进入事件分发关键
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();// Reset the key repeat timer whenever normal dispatch is suspended while the// device is in a non-interactive state.  This is to ensure that we abort a key// repeat if the device is just coming out of sleep.// 不允许事件分发 重置参数if (!mDispatchEnabled) {resetKeyRepeatLocked();}// If dispatching is frozen, do not process timeouts or try to deliver any new events.if (mDispatchFrozen) {if (DEBUG_FOCUS) {ALOGD("Dispatch frozen.  Waiting some more.");}return;}// Optimize latency of app switches.// Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has// been pressed.  When it expires, we preempt dispatch and drop all other pending events.// 这里是优化 app 切换的延迟// mAppSwitchDueTime 是 app 切换的超时时间,如果小于当前时间,那么表明app切换超时了bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;if (mAppSwitchDueTime < *nextWakeupTime) {*nextWakeupTime = mAppSwitchDueTime;}// Ready to start a new event.// If we don't already have a pending event, go grab one.// 判断是否有进行的事件,没有就进入,并取iq队列if (!mPendingEvent) {if (mInboundQueue.empty()) {if (isAppSwitchDue) {// The inbound queue is empty so the app switch key we were waiting// for will never arrive.  Stop waiting for it.resetPendingAppSwitchLocked(false);isAppSwitchDue = false;}// Synthesize a key repeat if appropriate.if (mKeyRepeatState.lastKeyEntry) {if (currentTime >= mKeyRepeatState.nextRepeatTime) {mPendingEvent = synthesizeKeyRepeatLocked(currentTime);} else {if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {*nextWakeupTime = mKeyRepeatState.nextRepeatTime;}}}// Nothing to do if there is no pending event.if (!mPendingEvent) {return;}} else {// Inbound queue has at least one entry.// 拿出 iq第一个mPendingEvent = mInboundQueue.front();mInboundQueue.pop_front();traceInboundQueueLengthLocked();}// Poke user activity for this event.// 如果这个事件需要传递给用户,那么需要同上层的 PowerManagerService,此时有用户行为,这个作用就是延长亮屏的时间if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {pokeUserActivityLocked(*mPendingEvent);}}// Now we have an event to dispatch.// All events are eventually dequeued and processed this way, even if we intend to drop them.ALOG_ASSERT(mPendingEvent != nullptr);bool done = false;DropReason dropReason = DropReason::NOT_DROPPED;if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {// 被截断策略截断dropReason = DropReason::POLICY;} else if (!mDispatchEnabled) {dropReason = DropReason::DISABLED;}if (mNextUnblockedEvent == mPendingEvent) {mNextUnblockedEvent = nullptr;}switch (mPendingEvent->type) {...case EventEntry::Type::MOTION: {std::shared_ptr<MotionEntry> motionEntry =std::static_pointer_cast<MotionEntry>(mPendingEvent);if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {// app 切换超时,导致触摸事件被丢弃dropReason = DropReason::APP_SWITCH;}if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {// 10s 之前的事件,已经过期dropReason = DropReason::STALE;}// 这里是优化应用无响应的一个措施,会丢弃mNextUnblockedEvent之前的所有触摸事件if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {dropReason = DropReason::BLOCKED;}// 分发触摸事件done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);break;}...}// 3. 如果事件被处理,重置一些状态,例如 mPendingEvent// 返回 true,就表示已经处理了事件// 事件被丢弃,或者发送完毕,都会返回 true// 返回 false,表示暂时不知道如何处理事件,因此线程会休眠// 然后,线程再次被唤醒时,再来处理这个事件if (done) {if (dropReason != DropReason::NOT_DROPPED) {dropInboundEventLocked(*mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately}
}

进入 dispatchMotionLocked 后会先通过 dropReason 原因进行判断事件是否要分发。继续分发后判断是否触摸事件。是的话 进入 findTouchedWindowTargetsLocked 查找触摸到的屏幕返回对应的结果。详细的获取屏幕流程在另一篇文章。然后判断是否通过投屏,如activityVIew。是否要添加对应的触摸。没有的话在进入 dispatchEventLocked。进行下一步的分发。

// 进行触摸事件分发处理
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {ATRACE_CALL();// Preprocessing.if (!entry->dispatchInProgress) {entry->dispatchInProgress = true;logOutboundMotionDetails("dispatchMotion - ", *entry);}// Clean up if dropping the event.// 1. 触摸事件有原因需要丢弃,那么不走后面的分发流程if (*dropReason != DropReason::NOT_DROPPED) {setInjectionResult(*entry,*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED: InputEventInjectionResult::FAILED);return true;}bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;// Identify targets.std::vector<InputTarget> inputTargets;bool conflictingPointerActions = false;InputEventInjectionResult injectionResult;if (isPointerEvent) {// Pointer event.  (eg. touchscreen)// 寻找触摸的窗口,窗口保存到 inputTargets// 2. 为触摸事件,寻找触摸的窗口// 触摸的窗口保存到 inputTargets 中injectionResult =findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,&conflictingPointerActions);} else {// Non touch event.  (eg. trackball)injectionResult =findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);}if (injectionResult == InputEventInjectionResult::PENDING) {return false;}// 注入输入事件相关setInjectionResult(*entry, injectionResult);if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));return true;}if (injectionResult != InputEventInjectionResult::SUCCEEDED) {CancelationOptions::Mode mode(isPointerEvent? CancelationOptions::CANCEL_POINTER_EVENTS: CancelationOptions::CANCEL_NON_POINTER_EVENTS);CancelationOptions options(mode, "input event injection failed");synthesizeCancelationEventsForMonitorsLocked(options);return true;}// Add monitor channels from event's or focused display.// 添加全局手势监听addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));if (isPointerEvent) {std::unordered_map<int32_t, TouchState>::iterator it =mTouchStatesByDisplay.find(entry->displayId);if (it != mTouchStatesByDisplay.end()) {const TouchState& state = it->second;// 判断是否有门户 即投屏窗口是否要将去全局事件传入给另一个屏幕if (!state.portalWindows.empty()) {// The event has gone through these portal windows, so we add monitoring targets of// the corresponding displays as well.for (size_t i = 0; i < state.portalWindows.size(); i++) {const WindowInfo* windowInfo = state.portalWindows[i]->getInfo();addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,-windowInfo->frameLeft, -windowInfo->frameTop);}}}}// Dispatch the motion.// 是否有冲突事件if (conflictingPointerActions) {CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,"conflicting pointer actions");synthesizeCancelationEventsForAllConnectionsLocked(options);}dispatchEventLocked(currentTime, entry, inputTargets);return true;
}

dispatchEventLocked 方法 通过 pokeUserActivityLocked 对屏幕进行亮屏。在通过遍历对应的connection 调用prepareDispatchCycleLocked 开始将对应的数据放进去oq

// 找到对应的connection 进行分发
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,std::shared_ptr<EventEntry> eventEntry,const std::vector<InputTarget>& inputTargets) {ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLEALOGD("dispatchEventToCurrentInputTargets");
#endifupdateInteractionTokensLocked(*eventEntry, inputTargets);ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true// 保持屏幕活动状态亮屏pokeUserActivityLocked(*eventEntry);// 遍历对应的目标控件for (const InputTarget& inputTarget : inputTargets) {sp<Connection> connection =getConnectionLocked(inputTarget.inputChannel->getConnectionToken());if (connection != nullptr) {// 遍历 开始通过connect 派发 队列入队prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);} else {if (DEBUG_FOCUS) {ALOGD("Dropping event delivery to target with channel '%s' because it ""is no longer registered with the input dispatcher.",inputTarget.inputChannel->getName().c_str());}}}
}

pokeUserActivityLocked 会判断是否cancel 事件不处理,最终构建 CommandEntry 放进命令数组,在下一次循环 执行 doPokeUserActivityLockedInterruptible 方法。该方法会调用对应的 powerManger 服务进行操作。

void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {if (eventEntry.type == EventEntry::Type::FOCUS ||eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||eventEntry.type == EventEntry::Type::DRAG) {// Focus or pointer capture changed events are passed to apps, but do not represent user// activity.return;}int32_t displayId = getTargetDisplayId(eventEntry);sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);if (focusedWindowHandle != nullptr) {const WindowInfo* info = focusedWindowHandle->getInfo();if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
#if DEBUG_DISPATCH_CYCLEALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
#endifreturn;}}int32_t eventType = USER_ACTIVITY_EVENT_OTHER;switch (eventEntry.type) {case EventEntry::Type::MOTION: {const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);// cancel事件不处理if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {return;}if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {eventType = USER_ACTIVITY_EVENT_TOUCH;}break;}}// 构建命令数组,执行 doPokeUserActivityLockedInterruptible 方法std::unique_ptr<CommandEntry> commandEntry =std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);commandEntry->eventTime = eventEntry.eventTime;commandEntry->userActivityEventType = eventType;commandEntry->displayId = displayId;postCommandLocked(std::move(commandEntry));
}// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) {ATRACE_CALL();android_server_PowerManagerService_userActivity(eventTime, eventType, displayId);
}

3.进入oq

先判断分屏,在通过 enqueueDispatchEntriesLocked 进入下个方法。

// 开始调用进入oq 队列
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {...// Split a motion event if needed.// 判断是否分屏触摸if (inputTarget.flags & InputTarget::FLAG_SPLIT) {LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,"Entry type %s should not have FLAG_SPLIT",NamedEnum::string(eventEntry->type).c_str());const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {std::unique_ptr<MotionEntry> splitMotionEntry =splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);if (!splitMotionEntry) {return; // split event was dropped}if (DEBUG_FOCUS) {ALOGD("channel '%s' ~ Split motion event.",connection->getInputChannelName().c_str());logOutboundMotionDetails("  ", *splitMotionEntry);}enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),inputTarget);return;}}// Not splitting.  Enqueue dispatch entries for the event as is.enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

进入判断后,先获取connection的 oq 是否为空,在传入不同的事件类型到 enqueueDispatchEntryLocked 添加到 oq,最后判断前后是否不相等,不相等则调用 startDispatchCycleLocked 继续派发。

// 进入oq 排列
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {...bool wasEmpty = connection->outboundQueue.empty();// 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.// 开始对应oq进行派发事件if (wasEmpty && !connection->outboundQueue.empty()) {startDispatchCycleLocked(currentTime, connection);}
}

判断对应的事件类型是否和分发是否一致,在创建对应的 DispatchEntry ,最后在放进去 connection的 oq 里面。

// 将对应的事件添加到connection的oq
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget,int32_t dispatchMode) {if (ATRACE_ENABLED()) {std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",connection->getInputChannelName().c_str(),dispatchModeToString(dispatchMode).c_str());ATRACE_NAME(message.c_str());}int32_t inputTargetFlags = inputTarget.flags;// 如果跟目标不一致 就不执行队列了if (!(inputTargetFlags & dispatchMode)) {return;}inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;// This is a new event.// Enqueue a new dispatch entry onto the outbound queue for this connection.// 创建要放进去队列的对象std::unique_ptr<DispatchEntry> dispatchEntry =createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a// different EventEntry than what was passed in.EventEntry& newEntry = *(dispatchEntry->eventEntry);// Apply target flags and update the connection's input state.switch (newEntry.type) {...case EventEntry::Type::MOTION: {const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);// Assign a default value to dispatchEntry that will never be generated by InputReader,// and assign a InputDispatcher value if it doesn't change in the if-else chain below.constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =static_cast<int32_t>(IdGenerator::Source::OTHER);dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;} else {dispatchEntry->resolvedAction = motionEntry.action;dispatchEntry->resolvedEventId = motionEntry.id;}if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&!connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,motionEntry.displayId)) {
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter ""event",connection->getInputChannelName().c_str());
#endif// We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because// this is a one-to-one event conversion.dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;}dispatchEntry->resolvedFlags = motionEntry.flags;if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;}if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;}if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion ""event",connection->getInputChannelName().c_str());
#endifreturn; // skip the inconsistent event}dispatchEntry->resolvedEventId =dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID? mIdGenerator.nextId(): motionEntry.id;if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) {std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32") to MotionEvent(id=0x%" PRIx32 ").",motionEntry.id, dispatchEntry->resolvedEventId);ATRACE_NAME(message.c_str());}if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&(motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {// Skip reporting pointer down outside focus to the policy.break;}dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,inputTarget.inputChannel->getConnectionToken());break;}...}// Remember that we are waiting for this dispatch to complete.if (dispatchEntry->hasForegroundTarget()) {incrementPendingForegroundDispatches(newEntry);}// Enqueue the dispatch entry.// 放进去对应的connection oq里面connection->outboundQueue.push_back(dispatchEntry.release());traceOutboundQueueLength(*connection);
}

4. 发布消息,并将数据放进去wq

开始判断对应的 connection 是否正常和oq是否为空。 赋值对应的分发时间 deliveryTime和超时时间timeoutTime, 为后续的anr进行判断。在通过 connection->inputPublisher.publishMotionEvent 。将事件进行派发。最后在将对应的数据移出oq, 并放进去connection的wq

// 开始进行分发
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {...while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {DispatchEntry* dispatchEntry = connection->outboundQueue.front();// 记录派发时间,为后面anr计时dispatchEntry->deliveryTime = currentTime;const std::chrono::nanoseconds timeout =getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());dispatchEntry->timeoutTime = currentTime + timeout.count();// Publish the event.status_t status;const EventEntry& eventEntry = *(dispatchEntry->eventEntry);switch (eventEntry.type) {...case EventEntry::Type::MOTION: {const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);PointerCoords scaledCoords[MAX_POINTERS];const PointerCoords* usingCoords = motionEntry.pointerCoords;// Set the X and Y offset and X and Y scale depending on the input source.if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {float globalScaleFactor = dispatchEntry->globalScaleFactor;if (globalScaleFactor != 1.0f) {for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {scaledCoords[i] = motionEntry.pointerCoords[i];// Don't apply window scale here since we don't want scale to affect raw// coordinates. The scale will be sent back to the client and applied// later when requesting relative coordinates.scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,1 /* windowYScale */);}usingCoords = scaledCoords;}} else {// We don't want the dispatch target to know.if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {scaledCoords[i].clear();}usingCoords = scaledCoords;}}std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);// Publish the motion event.status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId,motionEntry.deviceId, motionEntry.source,motionEntry.displayId, std::move(hmac),dispatchEntry->resolvedAction,motionEntry.actionButton,dispatchEntry->resolvedFlags,motionEntry.edgeFlags, motionEntry.metaState,motionEntry.buttonState,motionEntry.classification,dispatchEntry->transform,motionEntry.xPrecision, motionEntry.yPrecision,motionEntry.xCursorPosition,motionEntry.yCursorPosition,dispatchEntry->displayOrientation,dispatchEntry->displaySize.x,dispatchEntry->displaySize.y,motionEntry.downTime, motionEntry.eventTime,motionEntry.pointerCount,motionEntry.pointerProperties, usingCoords);break;}...}// Check the result.if (status) {if (status == WOULD_BLOCK) {if (connection->waitQueue.empty()) {ALOGE("channel '%s' ~ Could not publish event because the pipe is full. ""This is unexpected because the wait queue is empty, so the pipe ""should be empty and we shouldn't have any problems writing an ""event to it, status=%s(%d)",connection->getInputChannelName().c_str(), statusToString(status).c_str(),status);abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);} else {// Pipe is full and we are waiting for the app to finish process some events// before sending more events to it.
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ Could not publish event because the pipe is full, ""waiting for the application to catch up",connection->getInputChannelName().c_str());
#endif}} else {ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, ""status=%s(%d)",connection->getInputChannelName().c_str(), statusToString(status).c_str(),status);abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);}return;}// Re-enqueue the event on the wait queue.// 将oq事件移除 添加到wqconnection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),connection->outboundQueue.end(),dispatchEntry));traceOutboundQueueLength(*connection);connection->waitQueue.push_back(dispatchEntry);if (connection->responsive) {mAnrTracker.insert(dispatchEntry->timeoutTime,connection->inputChannel->getConnectionToken());}traceWaitQueueLength(*connection);}
}

publishMotionEvent 将 将数据转化为 InputMessage。并调用对应的 sendMessage 方法。通过socketPair 发送。至于**
**。

// frameworks/native/libs/input/InputTransport.cpp
// 发送对应的事件,通过InputChannel
status_t InputPublisher::publishMotionEvent(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 actionButton, int32_t flags,int32_t edgeFlags, int32_t metaState, int32_t buttonState,MotionClassification classification, const ui::Transform& transform, float xPrecision,float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,uint32_t pointerCount, const PointerProperties* pointerProperties,const PointerCoords* pointerCoords) {if (ATRACE_ENABLED()) {std::string message = StringPrintf("publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",mChannel->getName().c_str(), action);ATRACE_NAME(message.c_str());}if (DEBUG_TRANSPORT_ACTIONS) {std::string transformString;transform.dump(transformString, "transform", "        ");ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, ""displayId=%" PRId32 ", ""action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, ""metaState=0x%x, buttonState=0x%x, classification=%s,""xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", ""pointerCount=%" PRIu32 " \n%s",mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,flags, edgeFlags, metaState, buttonState,motionClassificationToString(classification), xPrecision, yPrecision, downTime,eventTime, pointerCount, transformString.c_str());}if (!seq) {ALOGE("Attempted to publish a motion event with sequence number 0.");return BAD_VALUE;}if (pointerCount > MAX_POINTERS || pointerCount < 1) {ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %" PRIu32 ".",mChannel->getName().c_str(), pointerCount);return BAD_VALUE;}InputMessage msg;msg.header.type = InputMessage::Type::MOTION;msg.header.seq = seq;msg.body.motion.eventId = eventId;msg.body.motion.deviceId = deviceId;msg.body.motion.source = source;msg.body.motion.displayId = displayId;msg.body.motion.hmac = std::move(hmac);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.classification = classification;msg.body.motion.dsdx = transform.dsdx();msg.body.motion.dtdx = transform.dtdx();msg.body.motion.dtdy = transform.dtdy();msg.body.motion.dsdy = transform.dsdy();msg.body.motion.tx = transform.tx();msg.body.motion.ty = transform.ty();msg.body.motion.xPrecision = xPrecision;msg.body.motion.yPrecision = yPrecision;msg.body.motion.xCursorPosition = xCursorPosition;msg.body.motion.yCursorPosition = yCursorPosition;msg.body.motion.displayOrientation = displayOrientation;msg.body.motion.displayWidth = displayWidth;msg.body.motion.displayHeight = displayHeight;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]);}return mChannel->sendMessage(&msg);
}

这里通过 send 方法将事件发送出去

// 发送消息 通过socket
status_t InputChannel::sendMessage(const InputMessage* msg) {// 深拷贝const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);ssize_t nWrite;do {nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);if (nWrite < 0) {int error = errno;
#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),msg->header.type, strerror(error));
#endifif (error == EAGAIN || error == EWOULDBLOCK) {return WOULD_BLOCK;}if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {return DEAD_OBJECT;}return -error;}if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ error sending message type %d, send was incomplete",mName.c_str(), msg->header.type);
#endifreturn DEAD_OBJECT;}#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type);
#endifreturn OK;
}

5. 接收消息

通过对 activity 的dispatchTouchEvent 事件打对应的断点查看堆栈可以看到。接收事件一开始入口是到了 frameworks/base/core/java/android/view/InputEventReceiver.javadispatchInputEvent
在这里插入图片描述
在这里插入图片描述

通过命令 grep dispatchInputEvent ./ -rn 因为堆栈可以看出最终来自 native方法,所以只留意cpp。可以看到即为 frameworks/base/core/java/android/view/InputEventReceiver.java
在这里插入图片描述
他是如何接收的,因为 InputEventReceiver 构造函数会调用 nativeInit 方法。

public InputEventReceiver(InputChannel inputChannel, Looper looper) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null");}if (looper == null) {throw new IllegalArgumentException("looper must not be null");}mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);mCloseGuard.open("dispose");}

nativeInit 会创建对应的 NativeInputEventReceiver 对象并调用对应的 initialize 方法。

// 初始化的时候 创建对应的 NativeInputEventReceiver 并通过initialize注册对应的监听
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {std::shared_ptr<InputChannel> inputChannel =android_view_InputChannel_getInputChannel(env, inputChannelObj);...sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();...receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}

initialize 方法会调用对应 setFdEvents 方法,进行注册对应socket文件监听。

// 初始化的时候注册对应的监听
status_t NativeInputEventReceiver::initialize() {setFdEvents(ALOOPER_EVENT_INPUT);return OK;
}// 注册对应的事件分发监听,也就是 inputChannel下创建的socketPair监听到对应looper,当监听到数据时候 会调用对应的 **handleEvent**
void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {mFdEvents = events;int fd = mInputConsumer.getChannel()->getFd();if (events) {mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);} else {mMessageQueue->getLooper()->removeFd(fd);}}
}

handleEvent 又会调用对应的 consumeEvents 方法。然后在调用android_view_MotionEvent_obtainAsCopy 获取对应的java对象 MotionEvent 继而通过jni 回调对应的java接口 dispatchInputEvent

// 通过looperCallback 回调该接口
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {...if (events & ALOOPER_EVENT_INPUT) {JNIEnv* env = AndroidRuntime::getJNIEnv();// 处理事件status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;}...return KEEP_CALLBACK;
}status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {...for (;;) {uint32_t seq;InputEvent* inputEvent;// inputTransport的 consume方法status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);if (!skipCallbacks) {if (!receiverObj.get()) {receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));if (!receiverObj.get()) {ALOGW("channel '%s' ~ Receiver object was finalized ""without being disposed.", getInputChannelName().c_str());return DEAD_OBJECT;}}jobject inputEventObj;switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_MOTION: {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());}MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {*outConsumedBatch = true;}// 调用 java实体类的obtain方法获取新对象inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}...// 获取到对象不为空if (inputEventObj) {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());}// 调用 调用 android/view/InputEventReceiver 的 dispatchInputEvent 方法env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);if (env->ExceptionCheck()) {ALOGE("Exception dispatching input event.");skipCallbacks = true;}env->DeleteLocalRef(inputEventObj);} else {ALOGW("channel '%s' ~ Failed to obtain event object.",getInputChannelName().c_str());skipCallbacks = true;}}if (skipCallbacks) {mInputConsumer.sendFinishedSignal(seq, false);}}
}

6. 移除wq

事件收到后 会通过责任链的模式 通过不同的 InputStage 处理事件,最终调用 finishInputEvent 开始通知事件结束,移除wq。finishInputEvent 本质也是调用 nativeFinishInputEvent 到 native 层处理,

public final void finishInputEvent(InputEvent event, boolean handled) {if (event == null) {throw new IllegalArgumentException("event must not be null");}if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to finish an input event but the input event "+ "receiver has already been disposed.");} else {int index = mSeqMap.indexOfKey(event.getSequenceNumber());if (index < 0) {Log.w(TAG, "Attempted to finish an input event that is not in progress.");} else {int seq = mSeqMap.valueAt(index);mSeqMap.removeAt(index);// 通知事件结束nativeFinishInputEvent(mReceiverPtr, seq, handled);}}event.recycleIfNeededAfterDispatch();}

会获取本身NativeInputEventReceiver 然后在调用 finishInputEvent

// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,jint seq, jboolean handled) {sp<NativeInputEventReceiver> receiver =reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);status_t status = receiver->finishInputEvent(seq, handled);if (status == OK || status == WOULD_BLOCK) {return; // normal operation}if (status != DEAD_OBJECT) {std::string message =android::base::StringPrintf("Failed to finish input event.  status=%s(%d)",statusToString(status).c_str(), status);jniThrowRuntimeException(env, message.c_str());}
}

会构造对应的finish对象,放进数组,在调用 processOutboundEvents,开始遍历数组。

// 通知事件结束移除掉
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());}Finish finish{.seq = seq,.handled = handled,};mOutboundQueue.push_back(finish);return processOutboundEvents();
}

因为构造的是finish对象,所以最终调用了 sendFinishedSignal 进行消息发送。

 // 开始遍历数组
status_t NativeInputEventReceiver::processOutboundEvents() {while (!mOutboundQueue.empty()) {OutboundEvent& outbound = *mOutboundQueue.begin();status_t status;...if (std::holds_alternative<Finish>(outbound)) {const Finish& finish = std::get<Finish>(outbound);status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);} ...}// The queue is now empty. Tell looper there's no more output to expect.setFdEvents(ALOOPER_EVENT_INPUT);return OK;
}

sendFinishedSignal 又会继续调用 sendUnchainedFinishedSignal

status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {...// Send finished signal for the last message in the batch.return sendUnchainedFinishedSignal(seq, handled);
}

构造对应 InputMessage 对象, 也是通过 inputChannel 的 sendMessage进行socket消息发送。

// 组装对应的消息通过inputChannel 发送对应的消息, 在inputDispatcher接收并处理
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {InputMessage msg;msg.header.type = InputMessage::Type::FINISHED;msg.header.seq = seq;msg.body.finished.handled = handled;msg.body.finished.consumeTime = getConsumeTime(seq);status_t result = mChannel->sendMessage(&msg);if (result == OK) {// Remove the consume time if the socket write succeeded. We will not need to ack this// message anymore. If the socket write did not succeed, we will try again and will still// need consume time.popConsumeTime(seq);}return result;
}
// 发送消息
status_t InputChannel::sendMessage(const InputMessage* msg) {// 深拷贝const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);ssize_t nWrite;do {nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);...
}

native 端接收消息是通过 window setView的时候通过 createInputChannel 创建对应的socketPair 。创建的时候也注册了对应的 消息监听。也就是 handleReceiveCallback 方法,

// 创建对应的socketPair inputChannel的时候创建, 窗口创建serView的时候
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATIONALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endifstd::unique_ptr<InputChannel> serverChannel;std::unique_ptr<InputChannel> clientChannel;status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);if (result) {return base::Error(result) << "Failed to open input channel pair with name " << name;}{ // acquire lockstd::scoped_lock _l(mLock);const sp<IBinder>& token = serverChannel->getConnectionToken();int fd = serverChannel->getFd();sp<Connection> connection =new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {ALOGE("Created a new connection, but the token %p is already known", token.get());}mConnectionsByToken.emplace(token, connection);// 用于绑定函数参数。它创建了一个可调用对象 std::placeholders::_1 是一个参数占位符号, token的第二个参数代表connectstd::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,this, std::placeholders::_1, token);// 添加对应的回调mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);} // release lock// Wake the looper because some connections have changed.mLooper->wake();return clientChannel;
}

收到对应的消息后,会判断对应的是不是 finish 类型,是的话进入 finishDispatchCycleLocked 进行处理。

// 接收结束事件的回调
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {std::scoped_lock _l(mLock);sp<Connection> connection = getConnectionLocked(connectionToken);if (connection == nullptr) {ALOGW("Received looper callback for unknown input channel token %p.  events=0x%x",connectionToken.get(), events);return 0; // remove the callback}bool notify;// 不属于这2种if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {if (!(events & ALOOPER_EVENT_INPUT)) {ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  ""events=0x%x",connection->getInputChannelName().c_str(), events);return 1;}nsecs_t currentTime = now();bool gotOne = false;status_t status = OK;for (;;) {Result<InputPublisher::ConsumerResponse> result =connection->inputPublisher.receiveConsumerResponse();if (!result.ok()) {status = result.error().code();break;}// 是否存储了指定的类型,结束事件会进去这里if (std::holds_alternative<InputPublisher::Finished>(*result)) {const InputPublisher::Finished& finish =std::get<InputPublisher::Finished>(*result);finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,finish.consumeTime);} else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {if (shouldReportMetricsForConnection(*connection)) {const InputPublisher::Timeline& timeline =std::get<InputPublisher::Timeline>(*result);mLatencyTracker.trackGraphicsLatency(timeline.inputEventId,connection->inputChannel->getConnectionToken(),std::move(timeline.graphicsTimeline));}}gotOne = true;}...}...
}

finishDispatchCycleLocked 又会调用 onDispatchCycleFinishedLocked

void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, uint32_t seq,bool handled, nsecs_t consumeTime) {
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",connection->getInputChannelName().c_str(), seq, toString(handled));
#endifif (connection->status == Connection::STATUS_BROKEN ||connection->status == Connection::STATUS_ZOMBIE) {return;}// Notify other system components and prepare to start the next dispatch cycle.onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
}

onDispatchCycleFinishedLocked 又会构建 CommandEntry 命令放进去数组 等待循环 执行 doDispatchCycleFinishedLockedInterruptible 方法。

// 构建对应的实体类 CommandEntry,其中方法作为command参数传进去,后续在数组里面遍历执行对应的doDispatchCycleFinishedLockedInterruptible方法
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,const sp<Connection>& connection, uint32_t seq,bool handled, nsecs_t consumeTime) {std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);commandEntry->connection = connection;commandEntry->eventTime = currentTime;commandEntry->seq = seq;commandEntry->handled = handled;commandEntry->consumeTime = consumeTime;// 放到指令列表postCommandLocked(std::move(commandEntry));
}

方法一开始通过 findWaitQueueEntry 找出wq对应的实体类,查找后判断对应的时间是否大于 大于则打印日志,然后在二次确认,确认好后从 wq 移除了该触摸消息。这样整个触摸消息流程就完整技术。

// 移除对应的显示
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {sp<Connection> connection = commandEntry->connection;const nsecs_t finishTime = commandEntry->eventTime;uint32_t seq = commandEntry->seq;const bool handled = commandEntry->handled;// Handle post-event policy actions.std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);if (dispatchEntryIt == connection->waitQueue.end()) {return;}DispatchEntry* dispatchEntry = *dispatchEntryIt;const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;// 如果大于2秒,则打印日志if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());}if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,connection->inputChannel->getConnectionToken(),dispatchEntry->deliveryTime, commandEntry->consumeTime,finishTime);}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));// 直接返回falserestartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,handled);} else {restartEvent = false;}// Dequeue the event and start the next cycle.// 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.// 二次判断dispatchEntryIt = connection->findWaitQueueEntry(seq);if (dispatchEntryIt != connection->waitQueue.end()) {dispatchEntry = *dispatchEntryIt;// 删除wqconnection->waitQueue.erase(dispatchEntryIt);const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);if (!connection->responsive) {connection->responsive = isConnectionResponsive(*connection);if (connection->responsive) {// The connection was unresponsive, and now it's responsive.processConnectionResponsiveLocked(*connection);}}traceWaitQueueLength(*connection);if (restartEvent && connection->status == Connection::STATUS_NORMAL) {connection->outboundQueue.push_front(dispatchEntry);traceOutboundQueueLength(*connection);} else {releaseDispatchEntry(dispatchEntry);}}// Start the next dispatch cycle for this connection.startDispatchCycleLocked(now(), connection);
}

相关文章:

frameworks 之InputDispatcher

frameworks 之InputDispatcher 1. 填充Iq2.进入循环3.进入oq4. 发布消息&#xff0c;并将数据放进去wq5. 接收消息6. 移除wq android 输入事件 主要分 2个流程 事件读取 和 事件分发。本文讲解事件分发流程。 涉及到的类如下 -frameworks/native/services/inputflinger/Input…...

ESP32-IDF GPIO 专题

目录 一、基本介绍1、配置结构体2、API2.1 gpio_config2.2 gpio_reset_pin2.3 gpio_set_intr_type2.4 gpio_intr_enable2.5 gpio_intr_disable2.6 gpio_set_level2.7 gpio_get_level2.8 gpio_set_direction2.9 gpio_set_pull_mode2.10 gpio_isr_register2.11 gpio_install_isr_…...

深度学习代码学习笔记2

1、torch.max correct 0 total 0 for xb,yb in valid_dl:outputs model(xb)_,predicted torch.max(outputs.data,1)total yb.size(0) #yb.size(0) 返回的是张量 yb 在第 0 维的大小&#xff0c;也就是 yb 中的样本数量。correct (predicted yb).sum().item() print(…...

016集——c# 实现CAD类库 与窗体的交互(CAD—C#二次开发入门)

第一步&#xff1a;搭建CAD类库dll开发环境。 第二步&#xff1a;添加窗体 第三步&#xff1a;添加控件 第四步&#xff1a;双击控件&#xff0c;在控件点击方法内输入代码 第五步&#xff1a;在主程序内实例化新建的form类&#xff0c;并弹窗form窗体 第六步&#xff1a;CAD命…...

【亲测可行】最新ubuntu搭建rknn-toolkit2

文章目录 🌕结构图(ONNX->RKNN)🌕下载rknn-toolkit2🌕搭建环境🌙配置镜像源🌙conda搭建python3.8版本的虚拟环境🌙进入packages目录安装依赖库🌕测试安装是否成功🌕其它🌙rknn-toolkit2🌙rknn_model_zoo🌙关于部署的博客发布本文的时间为2024.10.13…...

pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…...

16年408计算机网络

第一题&#xff1a; 解析&#xff1a; 首先我们要清楚R1,R2,R3是路由器&#xff08;网络层&#xff09;&#xff0c;Switch是以太网交换机&#xff08;数据链路层&#xff09;&#xff0c;Hub是集线器&#xff08;物理层&#xff09;。 由此可见路由器实现的最高功能层是3层&am…...

PDF 转 CAD 工具:实现文档格式高效转换的利器

如果你从事设计相关PDF和CAD作为两种常见且重要的文件格式&#xff0c;在不同的领域都有着广泛的应用。今天&#xff0c;我们就来介绍几个各具特色的PDF转换成CAD工具。 1.福昕PDF转换大师 链接一下>>https://www.pdf365.cn/pdf2word/ 该工具在跨领域应用中表现出明确…...

基于springboot的画师约稿系统的设计与实现

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于springboot的画师约稿系统的设计与实…...

使用Python生成SVG图片

SVG(可缩放矢量图形)是一种基于XML的图像格式,它可以无损缩放且文件大小较小。在本文中,我们将探讨如何使用Python生成SVG图片。 为什么选择SVG? 可缩放:SVG图像可以无限放大而不失真。文件小:SVG文件通常比位图文件小。可编辑:SVG文件可以通过文本编辑器修改。 使用Python…...

hackmyvm-Hundred靶机

主机发现 sudo arp-scan -l 以sudo权限执行arp-scan -l 扫描并列出本地存在的机器&#xff0c;发现靶机ip为192.168.91.153 nmap扫描 端口发现 21/tcp open ftp 22/tcp open ssh 80/tcp open http web信息收集 我们先尝试一下ftp端口的匿名登录 FTP:是文件传输协议的端…...

多场景多任务建模(三): M2M(Multi-Scenario Multi-Task Meta Learning)

多场景建模: STAR(Star Topology Adaptive Recommender) 多场景建模&#xff08;二&#xff09;: SAR-Net&#xff08;Scenario-Aware Ranking Network&#xff09; 前面两篇文章&#xff0c;讲述了关于多场景的建模方案&#xff0c;其中可以看到很多关于多任务学习的影子&…...

Day31 || 122.买卖股票的最佳时机 II、55. 跳跃游戏、 45.跳跃游戏II 、1005.K次取反后最大化的数组和

122.买卖股票的最佳时机 II 题目链接&#xff1a;力扣题目链接 思路&#xff1a;因为是求虽大利润完全可以假设知道第二天涨前一天买入即可&#xff0c;就是求两天只差大于0 的和。 55. 跳跃游戏 题目链接&#xff1a;力扣题目链接 思路&#xff1a;应该从后往前循环判断&…...

【uniapp】打包成H5并发布

目录 1、设置配置mainifest.sjon 1.1 页面标题 1.2 路由模式 1.3 运行的基础路径 2、打包 2.1 打包入口 2.2 打包成功 2.3 依据目录找到web目录 3、 将web目录整体拷贝出来 4、上传 4.1 登录uniapp官网注册免费空间 4.2 上传拷贝的目录 4.3 检查上传是否正确 5、…...

Position Embedding总结和Pytorch实现

文章目录 出现背景PE位置编码公式思路code 出现背景 自注意力机制处理数据&#xff0c;并不是采用类似RNN或者LSTM那种递归的结构&#xff0c;这使得模型虽然能够同时查看输入序列中的所有元素&#xff08;即并行运算&#xff09;&#xff0c;但是也导致了没办法获取当前word在…...

【AIF-C01认证】亚马逊云科技生成式 AI 认证正式上线啦

文章目录 一、AIF-C01简介二、考试概览三、考试知识点3.1 AI 和 ML 基础知识3.2 生成式人工智能基础3.3 基础模型的应用3.4 负责任 AI 准则3.5 AI 解决方案的安全性、合规性和监管 四、备考课程4.1 「备考训练营」 在线直播课4.2 「SkillBuilder」学习课程 五、常见问题六、参考…...

C++ 素数的筛选法与穷举法

题目:素数大酬宾&#xff1a; 【问题描述】 某商场的仓库中有 n 种商品&#xff0c;每件商品按 1~n 依次编号。现在商场经理突发奇想&#xff0c;决定将编号为素数&#xff08;质数&#xff09;的所有商品拿出来搞优惠酬宾活动。请编程帮助仓库管理员将编号为素数的商品选出来…...

Spring Boot异步任务、任务调度与异步请求线程池的使用及原理

Spring Boot异步任务、任务调度与异步请求线程池的使用及原理 在Spring Boot应用程序中&#xff0c;异步任务、任务调度和异步请求线程池是提高系统性能和响应速度的重要工具。本文将详细讲解这些概念的使用及原理。 一、异步任务 异步任务是指可以在后台线程上执行的任务&a…...

Java爬虫之使用Selenium WebDriver 爬取数据

这里写自定义目录标题 Selenium WebDriver简介一、安装部署二、Java项目中使用1.引入依赖2.示例代码 三、WebDriver使用说明1.WebDriver定位器2.常用操作3.使用 cookie4.键盘与鼠标操作 Selenium WebDriver简介 Selenium WebDriver 是一种用于自动化测试 Web 应用程序的工具。…...

MyBatis 中updateByPrimaryKey和updateByPrimaryKeySelective区别

在 MyBatis 中&#xff0c;updateByPrimaryKey和updateByPrimaryKeySelective主要有以下区别&#xff1a; 一、功能 updateByPrimaryKey&#xff1a; 会根据传入的实体对象&#xff0c;将数据库表中对应主键的记录所有字段全部更新为实体对象中的值。即使实体对象中的某些字段…...

JavaScript下载文件(简单模式、跨域问题、文件压缩)

文章目录 简介简单文件下载通过模拟form表单提交通过XMLHttpRequest方式 跨域(oss)下载并压缩文件完整示例文件压缩跨域设置 简介 相信各位开发朋友都遇到过下载的文件的需求&#xff0c;有的非常简单&#xff0c;基本链接的形式就可以。 有的就比较复杂&#xff0c;涉及跨域…...

Django 定义使用模型,并添加数据

教材&#xff1a; Python web企业级项目开发教程&#xff08;黑马程序员&#xff09;第三章 模型 实验步骤&#xff1a; 1.创建项目和应用 前置步骤可看前文&#xff0c;进入到指定文件位置后创建 django-admin startproject mysite python manage.py startapp app01 2.注册…...

联名物料常泄漏?一端叠满“安全buff”

前段时间&#xff0c;一则关于爆火影视剧与知名茶饮品牌联名的消息在社交平台上迅速传播&#xff0c;宣传物料的照片也随之曝光——门店尚未上新&#xff0c;“小道消息”便已被疯传。但这种情况并非首次发生&#xff0c;让众多网友不禁猜想&#xff1a;这究竟是一场精心策划的…...

Flutter UI组件库(JUI)

Flutter UI组件库 (JUI) 介绍 您是否正在寻找一种方法来简化Flutter开发过程&#xff0c;并创建美观、一致的用户界面&#xff1f;您的搜索到此为止&#xff01;我们的Flutter UI组件库&#xff08;JUI&#xff09;提供了广泛的预构建、可自定义组件&#xff0c;帮助您快速构建…...

国外电商系统开发-运维系统远程文件

设计初衷是为了让所有人都能方便的打开网页&#xff0c;就能查看Linux系统文件内容&#xff0c;而不再用cat、vim、more等命令去打开文件&#xff0c;这对于我们一个普通的研发或者是财务人员来说&#xff0c;显得太繁琐&#xff0c;因为他们很可能不会这些命令&#xff0c;其次…...

4. Node.js Path模块

2.3Path模块 2.3.1获取js文件的绝对路径 console.log(__dirname) //js文件所在的文件夹的绝对路径 console.log(__filename) //js文件的绝对路径输出&#xff1a; G:\py_project\nodejs_study G:\py_project\nodejs_study\file.js2.3.2拼接规范的绝对路径path.r…...

重构长方法之分解条件表达式

分解条件表达式 是一种重构长方法中常用的技术&#xff0c;它适用于复杂的条件逻辑。在方法中&#xff0c;条件分支&#xff08;if-else 或 switch&#xff09;有时会变得条件非常多&#xff0c;非常复杂&#xff0c;难以理解和维护。通过分解条件逻辑&#xff0c;可以让代码更…...

蚁群算法养老服务人员智能调度系统

养老行业近年来越发热门&#xff0c;如何有效调配服务人员成为许多机构的痛点。我们结合智能算法技术&#xff0c;开发出了一款专为养老行业量身打造的“蚁群算法养老服务人员调度系统”&#xff0c;能够精准、高效地为机构分配人员&#xff0c;从此告别人力资源调度难题。 系…...

java使用 IDEA自动补全功能 AI 插件

国内插件&#xff1a; CodeGeeX&#xff1a; 功能特性&#xff1a; 由国内团队开发&#xff0c;是一款智能编程助手插件。它集成了多种人工智能技术&#xff0c;能够在多个编程语言中提供智能代码补全、代码生成、代码优化和注释生成等功能。该插件特别适用于常见的编程任务…...

【ShuQiHere】 AI与自我意识:能否创造真正的自觉机器人?

&#x1f916;【ShuQiHere】 &#x1f4dc; 目录 引言人类意识的探索机器意识的五大理论 功能主义&#xff08;Functionalism&#xff09;信息整合&#xff08;Information Integration&#xff09;体现主义&#xff08;Embodiment&#xff09;行动主义&#xff08;Enaction&…...