以下分析基于Android S.
简述
在前面两篇文章中我们打通了应用进程和Input进程,这两者通过一对名为InputChannel实际是通过socket实现的通道来通信。然后我们又梳理了窗口信息是如何更新并传递给Input进程的。现在我们简单梳理一下一次触摸事件分发给窗口的过程。
事件要分发,首先是需要找到被分发的事件和对应的目标窗口。
Input事件是显示屏驱动收到中断后通知给InputReader,由其打包交给InputDispatcher分发的,我们直接看InputDispatcher的MotionEvent分发过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
...... bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; std::vector<InputTarget> inputTargets; if (isPointerEvent) { injectionResult = findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } ...... dispatchEventLocked(currentTime, entry, inputTargets); return true; } }
|
Android的输入源有如下几种:
名称 |
值 |
含义 |
AINPUT_SOURCE_CLASS_NONE |
0 |
未定义,交给应用自行处理 |
AINPUT_SOURCE_CLASS_BUTTON |
1 |
按键之类的设备 |
AINPUT_SOURCE_CLASS_POINTER |
2 |
输入源是一个与显示器相关联的指向设备,如触摸屏 |
AINPUT_SOURCE_CLASS_NAVIGATION |
4 |
输入源轨迹球导航设备 |
AINPUT_SOURCE_CLASS_POSITION |
8 |
输入源是与显示器无关的绝对定位装置,如触控板 |
AINPUT_SOURCE_CLASS_JOYSTICK |
16 |
输入源是一个操纵杆,摇杆设备 |
当然,MotionEvent肯定是来自AINPUT_SOURCE_CLASS_POINTER了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ...... sp<InputWindowHandle> newTouchedWindowHandle; ...... TouchState tempTouchState; ...... bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); ...... if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { int32_t x; int32_t y; ...... x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); ...... newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isDown , true ); ...... if (newTouchedWindowHandle != nullptr) { ...... tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } ....... } ...... for (const TouchedWindow& touchedWindow : tempTouchState.windows) { addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, touchedWindow.pointerIds, inputTargets); } ...... }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets, bool addPortalWindows, bool ignoreDragWindow) { ...... const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { ...... const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { auto flags = windowInfo->flags; if (windowInfo->visible) { if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) { bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) && !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL); if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { ...... return windowHandle; } } ...... } } } return nullptr; }
|
首先从mWindowHandlesByDisplay中拿到当前该display中所有的窗口信息,依次遍历,判断窗口是否可见,不可见窗口无法接收input事件。如果可见,在判断窗口是否携带NOT_TOUCHABLE的flag。如果不携带,只要触摸事件的坐标位于该窗口的可触摸区域内,就返回该窗口。
注意getWindowHandlesLocked拿到的窗口是有顺序的,index越小,Z轴越大。因为在SurfaceFlinger.updateInputWindowInfo时我们遍历layer的顺序是沿着Z轴反向遍历的。那么窗口在上面的会存储在mWindowHandlesByDisplay中对应队列的前面,所以这里只需要找到第一个窗口返回即可。
1 2 3 4 5 6
| const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES; auto it = mWindowHandlesByDisplay.find(displayId); return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES; }
|
从mWindowHandlesByDisplay中拿到当前该display中所有的窗口信息(WMS中addWindow后添加的)。
1 2 3
| bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { return touchableRegion.contains(x,y); }
|
判断该窗口的可触摸区域是否包含此坐标点。
1.4 TouchState.addOrUpdateWindow
1 2 3 4 5 6 7 8 9 10
| void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds) { ...... TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; touchedWindow.targetFlags = targetFlags; touchedWindow.pointerIds = pointerIds; windows.push_back(touchedWindow); }
|
将找到的目标窗口封装成TouchedWindow存入TouchState.windows队列的末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { 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; std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); if (inputChannel == nullptr) { ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); return; } inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; inputTarget.displaySize = vec2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; }
ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); it->addPointers(pointerIds, windowInfo->transform); }
|
这里也是比较简单的一个函数,首先使用find_if函数,查找inputTargets中inputChannel的token和传入的窗口token一致的InputTarget,如果不存在就根据传入的窗口信息windowHandle创键新的InputTarget并存入inputTargets队列的末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> eventEntry, const std::vector<InputTarget>& inputTargets) { ...... pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { 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()); } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; }
for (const auto& [token, connection] : mConnectionsByToken) { if (token == inputConnectionToken) { return connection; } }
return nullptr; }
|
传入的token就是InputChannel初始化时创建BBinder, 而InputChannel创建完成后会生成Connection存入mConnectionsByToken,这个发生在InputDispatcher::createInputChannel中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget) { ...... enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); }
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget) { ...... bool wasEmpty = connection->outboundQueue.empty(); ...... enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_IS); ...... if (wasEmpty && !connection->outboundQueue.empty()) { startDispatchCycleLocked(currentTime, connection); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { ...... while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); ...... const EventEntry& eventEntry = *(dispatchEntry->eventEntry); switch (eventEntry.type) { ...... case EventEntry::Type::MOTION: { ...... status = connection->inputPublisher .publishMotionEvent(......); ...... }
|
当InputChannel对应的连接正常,则将待分发队列里的事件一一分发,每次都取outboundQueue中的第一个事件,保证先进先出。
Connection中的InputPublisher就是Connection初始化时创建的InputPublisher。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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, int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { ...... InputMessage msg; msg.header.type = InputMessage::Type::MOTION; ...... msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; ...... return mChannel->sendMessage(&msg); }
|
将input事件封装成InputMessage, 在交给InputChannel将input事件通知给应用进程.
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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);
...... return OK; }
|
经过InputChannel, 此次的input事件就通知给了目标窗口所在进程。回忆一下,我们应用进程中注册该InputChannel对的Socket文件描述符是在NativeInputEventReceiver中的:
1 2 3 4 5 6 7 8 9 10 11 12
| 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); } } }
|
所以我们应用进程收到该socket文件描述符消息时,会调用NativeInputEventReceiver::handleEvent函数了(Looper机制)。
1 2 3 4 5 6 7 8 9 10 11
| int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { ...... if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false , -1, nullptr); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK; } ...... }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { ...... for (;;) { uint32_t seq; InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); ...... if (!skipCallbacks) { ...... jobject inputEventObj; switch (inputEvent->getType()) { ...... case AINPUT_EVENT_TYPE_MOTION: { MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent); if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) { *outConsumedBatch = true; } inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent); break; } ...... } if (inputEventObj) { env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); ...... env->DeleteLocalRef(inputEventObj); } } } }
|
consumeEvents就是接收socket数据,将input事件封装成对应类型的对象,如触摸事件对应MotionEvent,按键事件对应KeyEvent. 然后通过JNI创建对应java层的input事件对象,最后调用InputEventReceiver.dispatchInputEvent将Input事件分发给应用java层的View。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { ...... *outSeq = 0; *outEvent = nullptr; while (!*outEvent) { ...... status_t result = mChannel->receiveMessage(&mMsg); ...... switch (mMsg.header.type) { ...... case InputMessage::Type::MOTION: { ...... MotionEvent* motionEvent = factory->createMotionEvent(); if (!motionEvent) return NO_MEMORY; updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); } ...... } } return OK; }
|
这里首先在InputChannel中通过recv函数接收来自Input系统的Socket数据(InputMessage), 然后根据input类型分别封装成对应的input事件,比如MotionEvent、KeyEvent等。
1 2 3 4 5 6 7 8
| status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); ...... }
|
通过recv函数接收Socket数据, 还原成InputMessage.
1 2 3 4 5 6 7
| @SuppressWarnings("unused") @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private void dispatchInputEvent(int seq, InputEvent event) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); }
|
注意这里JNI注册的InputEventReceiver其实是ViewRootImpl中的WindowInputEventReceiver, 所以onInputEvent其实是调用了被Override的子类函数。
1 2 3 4 5 6 7 8
| @Override public void onInputEvent(InputEvent event) { ...... } else { enqueueInputEvent(event, this, 0, true); } }
|
后面将事件分发给对应的View组件的过程,其实也不难猜测,因为View是树形结构,只需要前序遍历该树找到input事件坐标所在的最叶子节点的View,如果该View消耗了此次事件,也就是设置了对应的Listener并实现了接口返回true, 那么该事件就不继续分发了。否则沿着树形结构依次遍历父View,看是否需要使用该事件。
到这里,Input和窗口的关系分析就告一段落了。接下来分析窗口对应的View和SurfaceFlinger通信过程。