Android 11 触控事件处理机制

1 概述

用户手指点击按压屏幕后,屏幕触控驱动产生中断,Linux内核会将硬件产生的触控事件包装成Event放在设备的dev/input/目录下。此后Android系统需要解决以下几个问题以实现整个触控事件的分发处理:

  1. 如何从设备上读取触控事件?
  2. 读取到触控事件后该如何派发事件?
  3. 派发事件时如何找到接收事件的目标应用窗口?
  4. 找到目标应用窗口后如何将事件传递到目标窗口?
  5. 目标应用窗口内部中的事件如何处理?

下面将结合最新Android 11系统源码,通过分析回答这些问题来了解Android系统触控事件处理机制的全貌。

2 触控事件的读取

Android所有的input输入设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,Linux内核便会将事件写到这些节点下,同时对于外部输入设备(鼠标、键盘等)的插拔还会引起这些节点的创建和删除。系统封装了一个叫EventHub的对象,它负责利用Linux的inotify和epoll机制监听/dev/input目录下的设备事件节点,通过EventHub的getEvents接口就可以监听并获取该事件

系统开机启动system_server进程时会创建启动InputManagerService核心服务,其中会先通过JNI调用创建InputManager对象,然后进一步新建一个InputReader对象;然后通过start方法为InputReader创建一个InputThread的Loop工作线程,这个线程的工作就是通过EventHub的getEvents监听读取Input事件。详细流程如下图所示:

InputReader.png

简要代码流程如下:

  /*frameworks/base/services/java/com/android/server/SystemServer.java*/
  private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
     ...
     // 1.先创建InputManagerService对象
     inputManager = new InputManagerService(context);
     ...
     // 2.start启动相关input线程
     inputManager.start();
     ...
  }

  /*frameworks/base/services/core/java/com/android/server/input/InputManagerService.java*/
   public InputManagerService(Context context) {
        ...
        // 1.JNI调用native接口完成InputManager初始化动作
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
        ...
    }

    public void start() {
        // 2.JNI调用native接口启动InputManager工作线程
        nativeStart(mPtr);
        ...
    }

 /*frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/
 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    ...
    // 创建NativeInputManager对象
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    ...
}

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    ...
    // 1.创建InputManager对象
    mInputManager = new InputManager(this, this);
    ...
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    ...
    // 2.调用InputManager的start函数
    status_t result = im->getInputManager()->start();
    ...
}

/*frameworks/native/services/inputflinger/InputManager.cpp*/
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    // 创建InputDispatcher触控事件分发对象
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    // 1.createInputReader创建InputReader事件读取对象,间接持有InputDispatcher的引用,以便事件读取完成后通知其进行事件分发
    mReader = createInputReader(readerPolicy, mClassifier);
}

status_t InputManager::start() {
    // 启动InputDispatcher工作线程
    status_t result = mDispatcher->start();
    ...
    // 2.启动InputReader工作线程
    result = mReader->start();
    ...
    return OK;
}

/*frameworks/native/services/inputflinger/reader/InputReader.cpp*/
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& listener)
      : mContext(this),
        // 初始化EventHub对象
        mEventHub(eventHub),
        mPolicy(policy),
        mGlobalMetaState(0),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 持有InputDispatcher的引用
    mQueuedListener = new QueuedInputListener(listener);
    ...
}

status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    // 创建名为“InputReader”的InputThread带loop工作线程,并在其中循环执行loopOnce函数
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
  return OK;
}

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { 
    ...
    // 1\. 从EventHub中监听读取触控事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { 
       ...
        if (count) {
            // 2.处理读取到的触控事件
            processEventsLocked(mEventBuffer, count);
        }
        ...
     }
    ...
    // 3.mQueuedListener其实就是InputDispatcher对象,flush会通知唤醒InputDispatcher分发事件
    mQueuedListener->flush();
}

通过上面流程,输入事件就可以被读取,经过InputReader::processEventsLocked被初步封装成RawEvent,最后通知InputDispatcher分发事件,下节继续分析事件的分发。

3 触控事件的分发

从上一节的代码流程分析中可以看到:在新建InputManager对象的时候,不仅创建了事件读取对象InputReader,还创建了事件分发对象InputDispatcher。InputReader在事件读取完毕后,会交给InputDispatcher去执行事件分发的逻辑,而InputDispatcher也拥有自己的独立的工作线程。这样设计的好处在于符合单一职责的设计思想,且事件的读取与分发工作在各自的线程中,处理效率更高,避免出现事件丢失。整个架构模型参见业界大神的一张图:

InputManager.png

接上一节代码分析,InputReader::loopOnce函数处理中,在触控事件读取完成后,最后一行会调用 mQueuedListener->flush()。mQueuedListener本质上就是InputDispatcher,所以调用flush动作就是通知InputDispatcher进行事件分发。InputDispatcher和InputReader一样,内部也有一个名为“InputDispatcher”的InputThread的Loop工作线程,当触控事件到来时其被唤醒处理事件。此处流程如下图所示:

InputDispatcher.png

简化的代码流程如下:

/*frameworks/native/services/inputflinger/InputListener.cpp*/
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // 触发调用notify
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    // 就是调用InputDispatcher的notifyMotion
    listener->notifyMotion(this);
}

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ...
    // 1.可以增加业务逻辑,在事件分发前做一些事情
    mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
    ...
    bool needWake;
    { 
      ...
      if (shouldSendMotionToInputFilterLocked(args)) {
            ...
            // 2\. filterInputEvent此处可以过滤触控事件,不再分发
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }
        }
        ...
        // 3.将触控事件放入"iq"队列等待分发
        needWake = enqueueInboundEventLocked(newEntry);
    } // release lock
    ...
    if (needWake) {
        // 4.唤醒loop工作线程,触发执行一次dispatchOnce逻辑
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(entry);
    // 触控事件放入mInboundQueue队列后,并增加"iq"的systrace tag信息
    traceInboundQueueLengthLocked();
    ...
    return needWake;
}

void InputDispatcher::traceInboundQueueLengthLocked() {
    if (ATRACE_ENABLED()) {
        // mInboundQueue队列对应的systrace tag为“iq”
        ATRACE_INT("iq", mInboundQueue.size());
    }
}

void InputDispatcher::dispatchOnce() {
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        ...
        if (!haveCommandsLocked()) {
            // 1.具体的事件分发逻辑封装在dispatchOnceInnerLocked中具体处理
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        ...
    } // release lock
    ...
    // 2.处理完本次事件分发逻辑后,loop工作线程进入休眠等待状态
    mLooper->pollOnce(timeoutMillis);
}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
    switch (mPendingEvent->type) {
        ...
        case EventEntry::Type::MOTION: {
            ...
            // 以Motion event屏幕触控事件类型为例,具体的事件分发处理逻辑封装在dispatchMotionLocked函数中
            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }
    ...
}

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ATRACE_CALL();
    ...
    if (isPointerEvent) {
        // 1\. findTouchedWindowTargetsLocked中找到具体接收处理此触控事件的目标窗口
        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions);
    } else {
        ...
    }
    ...
    // 2\. 将此次触控事件分发给目标应用窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

从以上代码可以看出,对于触控事件的处理核心逻辑分为两步:

  1. 首先通过findTouchedWindowTargetsLocked找到处理此次触控事件的目标应用窗口;
  2. 下来通过dispatchEventLocked将触控事件发送到目标窗口。

下面将分两节来分别分析讲解这两个步骤。

4 寻找触控事件的目标窗口

Android系统的Primary Display主显示屏上同一时间总是会存在多个可见窗口,如状态栏、导航栏、应用窗口、应用界面Dialog弹框窗口等。用adb shell dumpsys SurfaceFlinger命令可以看到:

Display 19260578257609346 HWC layers:
-----------------------------------------------------------------------------------------------------------------------------------------------
 Layer name
           Z |  Window Type |  Layer Class |  Comp Type |  Transform |   Disp Frame (LTRB) |          Source Crop (LTRB) |     Frame Rate (Explicit) [Focused]
-----------------------------------------------------------------------------------------------------------------------------------------------
 com.android.systemui.ImageWallpaper#0
  rel      0 |         2013 |            0 |     DEVICE |          0 |    0    0 1080 2400 |    0.0    0.0 1080.0 2400.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 cn.nubia.launcher/com.android.launcher3.Launcher#0
  rel      0 |            1 |            0 |     DEVICE |          0 |    0    0 1080 2400 |    0.0    0.0 1080.0 2400.0 |                              [*]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 FloatBar#0
  rel      0 |         2038 |            0 |     DEVICE |          0 | 1065  423 1080  623 |    0.0    0.0   15.0  200.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 StatusBar#0
  rel      0 |         2000 |            0 |     DEVICE |          0 |    0    0 1080  111 |    0.0    0.0 1080.0  111.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 NavigationBar0#0
  rel      0 |         2019 |            0 |     DEVICE |          0 |    0 2280 1080 2400 |    0.0    0.0 1080.0  120.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

那如何从这些窗口中找到处理触控事件的那个目标窗口呢?我们接着上一节的分析来看看InputDispatcher::findTouchedWindowTargetsLocked的简化代码逻辑:

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
                                                        const MotionEntry& entry,
                                                        std::vector<InputTarget>& inputTargets,
                                                        nsecs_t* nextWakeupTime,
                                                        bool* outConflictingPointerActions) {
    ATRACE_CALL();
    ...
    // 读取触控事件所属的Display的id信息
    int32_t displayId = entry.displayId;
    ...
    // 只有ACTION_DOWN类型代表新的触控事件,需要寻找新的目标窗口,后续的ACTION_MOVE或ACTION_UP类型的触控事件直接发送导已经找到的目标窗口
    bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                       maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
    ...

    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        // 读取触控事件的x、y坐标位置
        int32_t x;
        int32_t y;
        if (isFromMouse) {
            ...
        } else {
            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
        }
        // 根据触控事件所属的displayid、x/y坐标位置等属性信息,调用findTouchedWindowAtLocked找到目标窗口
        sp<InputWindowHandle> newTouchedWindowHandle =
                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
        ...
    } else {
        ...
    }
    ...
}

sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
																 bool isMirrorInject,/*Nubia add for mirror*/
                                                                 bool addOutsideTargets,
                                                                 bool addPortalWindows) {
    ...
    // 1.根据传入的displayid调用getWindowHandlesLocked找到同一display下的所有可见窗口集合windowHandles
    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
    // 2.遍历所有可见窗口windowHandles找到目标窗口
    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo->displayId == displayId) {
            int32_t flags = windowInfo->layoutParamsFlags;
            if (windowInfo->visible) {
                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                    ...
                    // 3.根据触控事件的x/y坐标位置信息与窗口的可见区域进行匹配找到目标窗口
                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                        ...
                        return windowHandle;
                    }
                }
            ...
            }
        }
    }
    return nullptr;
}

std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
        int32_t displayId) const {
    // 从集合mWindowHandlesByDisplay中根据传入的displayId取出对应的可见窗口集合
    return getValueByKey(mWindowHandlesByDisplay, displayId);
}

从上面的代码分析可以看到,整个findTouchedWindowTargetsLocked寻找目标窗口的大概逻辑就是根据读取到的触控事件所属的屏幕displayid、x、y坐标位置等属性,遍历从mWindowHandlesByDisplay中匹配找到目标窗口。那么问题来了,这个掌握着所有可见窗口信息的mWindowHandlesByDisplay集合信息又是从哪儿来的呢?我们接着看看代码:

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
void InputDispatcher::updateWindowHandlesForDisplayLocked(
        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
    ...
    // Insert or replace
    // 唯一给mWindowHandlesByDisplay集合赋值的地方
    mWindowHandlesByDisplay[displayId] = newHandles;
}

void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
    ...
    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
    ...
}

// 外部通过调用setInputWindows接口给mWindowHandlesByDisplay赋值
void InputDispatcher::setInputWindows(
        const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        for (auto const& i : handlesPerDisplay) {
            setInputWindowsLocked(i.second, i.first);
        }
    }
    ...
}

从以上代码分析可以知道,其实外部是通过调用InputDispatcher的setInputWindows接口给mWindowHandlesByDisplay赋值的。那又是谁会调用这个函数呢?早期的Android版本上是由系统框架的窗口“大管家”——WindowManagerService中的InputMonitor通过JNI调用的方式间接调用InputDispatcher::setInputWindows,以实现同步可见窗口信息给InputDispatcher。但是最新的Android 11上,这个逻辑改由SurfaceFlinger来完成。SurfaceFlinger会在每一帧合成任务SurfaceFlinger::OnMessageInvalidate中搜集每个可见窗口Layer的信息通过Binder调用InputDispatcher::setInputWindows同步可见窗口信息给InputDispatcher。详细流程如下图所示:

setInputWindows.png

个人理解google的工程师之所以会这么修改的原因可能是出于以下两点:1.触控事件的分发其实只关心所有可见窗口的信息,而SurfaceFlinger其实最清楚所有可见窗口的Layer的触控区域、透明度、图层Z_Order顺序等信息;2.这样WindowManagerService只需要统一负责将窗口信息同步给SurfaceFlinger即可,不再同时维护将可见窗口信息同步给InputDispatcher的逻辑,整体职责更加清晰。最后我们看看简化代码分析:

/*frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp*/
void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
    ATRACE_CALL();
    ...
    updateInputFlinger();
    ...
}

void SurfaceFlinger::updateInputFlinger() {
    ATRACE_CALL();
    if (!mInputFlinger) {
        return;
    }
    // 当判断存在窗口Layer图层dirty待更新脏区或Layer图层的触控属性发生变化时,调用updateInputWindowInfo同步窗口信息给InputDispatcher
    if (mVisibleRegionsDirty || mInputInfoChanged) {
        mInputInfoChanged = false;
        updateInputWindowInfo();
    } else if (mInputWindowCommands.syncInputWindows) {
        ...
    }
    ...
}

void SurfaceFlinger::updateInputWindowInfo() {
    std::vector<InputWindowInfo> inputHandles;
    // 1.遍历mDrawingState中的所有可见窗口Layer
    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
        if (layer->needsInputInfo()) {
            // When calculating the screen bounds we ignore the transparent region since it may
            // result in an unwanted offset.
            // 2\. 调用Layer::fillInputInfo接口,从每个Layer中读取其可见区域、透明度、触控区域等信息,并封装成InputWindowInfo对象放置到inputHandles集合中
            inputHandles.push_back(layer->fillInputInfo());
        }
    });
    // 3.通过Binder调用InputManager服务端的接口setInputWindows将封装好的可见窗口触控信息间接同步到InputDispatcher
    mInputFlinger->setInputWindows(inputHandles,
                                   mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
                                                                         : nullptr);
}

/*frameworks/native/services/surfaceflinger/Layer.cpp*/
InputWindowInfo Layer::fillInputInfo() {
    if (!hasInputInfo()) {
        mDrawingState.inputInfo.name = getName();
        mDrawingState.inputInfo.ownerUid = mCallingUid;
        mDrawingState.inputInfo.ownerPid = mCallingPid;
        mDrawingState.inputInfo.inputFeatures =
            InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
        mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
        mDrawingState.inputInfo.displayId = getLayerStack();
    }

    InputWindowInfo info = mDrawingState.inputInfo;
    ...
    // 同步Layer的触控区域信息
    info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
    ...
    return info;
}

5 触控事件发送到目标窗口

5.1 触控事件发送到目标窗口流程

到目前为止,我们已经读取到了触控事件,也找到了触控事件对应的目标窗口。那么下一个问题就是如何将触控事件通知发送到目标窗口?根据前面的分析可以知道,InputReader和InputDispatcher都是在系统system_server进程中创建各自的工作线程来开展具体工作的,而目标窗口又是属于App应用进程的,那么这里就涉及到跨进程通信的问题了。分析源码我们可以发现,目前Android系统采用的是Socket机制来实现跨进程将触控事件通知到目标应用窗口的。下面我们接着前面的分析,从InputDispatcher::dispatchEventLocked函数入手,分析系统是如何一步步实现将触控事件发送到目标窗口的。整个流程如下图所示:

InputChannel.png

简化的代码流程如下:

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
    ATRACE_CALL();
    ...
    // 遍历需要发送触控事件的目标窗口inputTargets
    for (const InputTarget& inputTarget : inputTargets) {
        // 获取目标窗口的连接
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            // 执行prepareDispatchCycleLocked完成具体的触控事件发送动作
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        } else {
            ...
        }
    }
}

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;
    }
    ...
    // 继续调用enqueueDispatchEntriesLocked
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   EventEntry* eventEntry,
                                                   const InputTarget& inputTarget) {
    ...
    // 判断目标窗口的connection的outboundQueue "oq"是否为空
    bool wasEmpty = connection->outboundQueue.empty();
    // 将触控事件放入目标窗口的outboundQueue队列中
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    ...                        
    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.empty()) {
        // 调用startDispatchCycleLocked进一步处理
        startDispatchCycleLocked(currentTime, connection);
    }
}

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
    ...
    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
        ...
        // 1.目标窗口连接状态正常,并且连接的outboundQueue队列不为空
        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
            ...
            case EventEntry::Type::MOTION: {
                ...
                // 2.判断为touch事件(我们本次分析暂时只关心这种类型的触控事件),调用publishMotionEvent函数分发按键
                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, xScale, yScale,
                                                     xOffset, yOffset, motionEntry->xPrecision,
                                                     motionEntry->yPrecision,
                                                     motionEntry->xCursorPosition,
                                                     motionEntry->yCursorPosition,
                                                     motionEntry->downTime, motionEntry->eventTime,
                                                     motionEntry->pointerCount,
                                                     motionEntry->pointerProperties, usingCoords);
                ...
            }
            ...
        }
        ...
        // 3.将处理完成的dispatchEntry从connection的outboundQueue移除,从“oq”队列移除
        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                                    connection->outboundQueue.end(),
                                                    dispatchEntry));
        // 更新systrace的相关“oq”队列tag打印						 
        traceOutboundQueueLength(connection);
        // 将outboundQueue队列的dispatchEntry放入connection的waitQueue队列,从“oq”队列移除,放入“wq”队列中等待应用进程处理
        connection->waitQueue.push_back(dispatchEntry);
        if (connection->responsive) {
            // 4.开启应用对触控事件处理的ANR追踪
            mAnrTracker.insert(dispatchEntry->timeoutTime,
                               connection->inputChannel->getConnectionToken());
        }
        // 更新systrace的相关“wq”队列tag打印		
        traceWaitQueueLength(connection);
    }
}

继续分析publishKeyEvent函数:

/*frameworks/native/libs/input/InputTransport.cpp*/
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, float xScale, float yScale, float xOffset,
        float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
        float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
    ...
    // 1.拿到输入事件的各种信息之后构造一个InputMessage
    InputMessage msg;
    msg.header.type = InputMessage::Type::MOTION;
    msg.body.motion.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.xScale = xScale;
    msg.body.motion.yScale = yScale;
    msg.body.motion.xOffset = xOffset;
    msg.body.motion.yOffset = yOffset;
    msg.body.motion.xPrecision = xPrecision;
    msg.body.motion.yPrecision = yPrecision;
    msg.body.motion.xCursorPosition = xCursorPosition;
    msg.body.motion.yCursorPosition = yCursorPosition;
    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]);
    }
    // 2.最后通过InputChannel将InputMessage分发给目标窗口
    return mChannel->sendMessage(&msg);
}

status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        // 通过socket机制,向mFd写入数据,发送触控事件到对端目标窗口进程中
        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    ...
    return OK;
}
复制代码

最后sendMessage的核心就是向mFd写入数据,mFd是什么呢?mFd其实就是一对socket的其中一个,InputChannel在构造的时候是一对,对应了一对socket,一个代表"client"端,一个代表"server"端,"server"端被注册到了InputDispatcher,"client"端返回给了APP进程,InputDispatcher和APP进程都会对自己的socket一端进行监听,所以APP进程和InputDispatcher就这样完成了通信。至于这个socket是如何创建与注册的,下一小节中我们会详细分析。

5.2 应用APP与InputDispatcher的InputChannel注册与监听

从上一节的分析中给我们了解到,触控事件是被InputDispatcher使用InputChannel封装的Socket发送到目标应用窗口所属的App应用进程的。那么这个InputChannel是什么时候创建和注册的呢?或者说InputDispatcher和APP之间是如何建立这种InputChannel连接的呢?回答这个问题我们需要从App应用启动时应用窗口创建的流程开始讲起:

一般App应用启动时,都需要调用WindowManagerGlobal::addView接口完成向系统WMS服务中添加应用窗口的的逻辑。还是老规矩,我们先放一张流程图来看看这个过程:

openInputChannel.png

进一步结合代码来看看这块流程:

/*frameworks/base/core/java/android/view/WindowManagerGlobal.java*/
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        ...
        synchronized (mLock) {
            ...
            // 创建ViewRootImpl实例并调用setView函数完成窗口添加的具体逻辑
            root = new ViewRootImpl(view.getContext(), display);
            ...
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                ...
            }
        }
}

 /*frameworks/base/core/java/android/view/ViewRootImpl.java*/   
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                ...
                InputChannel inputChannel = null;
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    // 1.创建一个空的InputChannel
                    inputChannel = new InputChannel();
                }
                try {
                    ...
                    // 2.Binder调用WMS的addToDisplayAsUser接口,并将InputChanel作为参数传入,以完成赋值
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                } catch (RemoteException e) {
                    ...
                }
                ...
                if (inputChannel != null) {
                    ...
                   // 3.根据赋值后的InputChannel封装创建WindowInputEventReceiver对象,以便后续接收input事件
                    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());
                }
                ...
            }
        }
    }

APP应用启动时通过addView添加应用窗口到WMS的过程中,会new一个空的InputChanel,然后通过Binder调用WMS的addToDisplayAsUser接口添加应用窗口时,顺带将其作为参数传到WMS,所以具体给InputChannel赋值是在WMS中完成的。下面我们继续看看WMS中的处理过程:

 /*frameworks/base/services/core/java/com/android/wm/Session.java*/
  @Override
  public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
        // 具体通过调用WMS的addWindow实现窗口的添加逻辑
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
                outInsetsState, outActiveControls, userId);
    }

  /*frameworks/base/services/core/java/com/android/wm/WindowManagerService.java*/
  public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
            ...
            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                // 调用WindowState的openInputChannel具体完成inputchannel的赋值动作
                win.openInputChannel(outInputChannel);
            }
            ...
        return res;
    }

   /*frameworks/base/services/core/java/com/android/wm/WindowState.java*/
   void openInputChannel(InputChannel outInputChannel) {
        ...
        // 应用窗口名称
        String name = getName();
        // 1.创建InputChannelPair,实现创建socketpair全双工通信信道
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];

        // 2.将服务端InputChannel注册到InputDispatcher中
        mWmService.mInputManager.registerInputChannel(mInputChannel);
        // 这个token唯一标识了接收input事件的App窗口(务必注意这个token保留在WMS中,会同步给SurfaceFlinger然后再到InputDispatcher中去,最终会用来匹配触控事件的目标窗口时使用,注册后,用于后续根据token匹配找到与App应用的connect连接)
        mInputWindowHandle.token = mInputChannel.getToken();
        if (outInputChannel != null) {
            // 3.将客户端InputChannel赋值给outInputChannel,并Binder回传给应用进程中ViewRootImpl中
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            ...
        }
        ...
    }

其实整个建立InputChannel连接过程的核心逻辑就是:

  1. 先通过InputChannel::openInputChannelPair创建socketpair全双工的通道,并分别填充到“server”服务端的InputChannel和“client”客户端的InputChannel中

  2. 再通过InputManager::registerInputChannel将“server”服务端的InputChannel注册到InputDispatcher中

  3. 最后通过InputChannel::transferTo通过Binder将outInputChannel回传到APP端,以便APP端那边实现监听input事件的逻辑

整个InputChannel信道的模型如下图所示:

Input信道模型.png

下面结合代码进一步分析这三个过程:

5.2.1 创建客户端与服务端的InputChannel

从InputChannel.openInputChannelPair入手我们继续分析:

   /*frameworks/base/core/java/android/view/InputChannel.java*/
   public static InputChannel[] openInputChannelPair(String name) {
        ...
        // 根据方法名我们知道,这是个典型的JNI调用实现,会调用native层的对应接口实现功能
        return nativeOpenInputChannelPair(name);
   }

   /*frameworks/base/core/jni/android_view_InputChannel.cpp*/
   static const JNINativeMethod gInputChannelMethods[] = {
    /* name, signature, funcPtr */
    { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
            (void*)android_view_InputChannel_nativeOpenInputChannelPair },
        ...
    }

    static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    ...
    // 1.构建一对native层的InputChannel
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    ...
    // 构造一个java层InputChannel类型数组
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
    ...
    // 2\. 将native层InputChannel转换为java层InputChannel
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
    ...
    // 将转换的java层InputChannel存到前面构造的java数组
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    // 3.返回给java层
    return channelPair;
}
复制代码

InputChannel::openInputChannelPair的主要逻辑就是JNI调用构造了一对native层InputChannel,然后再根据其创建java层InputChannel,最后返回给java层。我们继续往下看native层InputChannel创建的逻辑:

/*frameworks/native/libs/input/InputTransport.cpp*/
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    // 1.创建基于linux的socketpair全双工的socket通道
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ...
        return result;
    }
    // 目前buffer的大小SOCKET_BUFFER_SIZE的默认值是32*1024也就是32K
    int bufferSize = SOCKET_BUFFER_SIZE;
    // 2.设置socket发送和接收缓冲区大小为bufferSize 
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    // 3.创建BBinder,用于唯一标识APP进程的应用窗口
    sp<IBinder> token = new BBinder();

    std::string serverChannelName = name + " (server)";
    android::base::unique_fd serverFd(sockets[0]);
     // server端InputChannel保存了server服务端socket的fd,注意创建InputChannel时是会传入token唯一标识的
    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);

    std::string clientChannelName = name + " (client)";
    android::base::unique_fd clientFd(sockets[1]);
    // client端InputChannel保存了client客户端socket的fd,注意创建InputChannel时是会传入token唯一标识的
    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
    return OK;
}

先调用linux的标准接口socketpair创建了全双工的通信信道(这里socketpair的创建与访问其实是还是借助文件描述符),设置好传输buffer的大小,然后创建了客户端的InputChannel并持有client客户端的socket的fd,创建了服务端的InputChannel并持有server服务端的socket的fd。最终客户端和服务端的InputChannel都转换成java对象返回到WindowState中。

5.2.2 注册服务端InputChannel到InputDispatcher

我们回到WindowState::openInputChannel继续往下看registerInputChannel的逻辑:

 /*frameworks/base/services/core/java/com/android/server/input/InputManagerService.java*/
 public void registerInputChannel(InputChannel inputChannel) {
        ...
        // 根据方法名我们知道,这是个典型的JNI调用实现,会调用native层的对应接口实现功能
        nativeRegisterInputChannel(mPtr, inputChannel);
 }

 /*frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 1.获取native层的InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    ...
    // 2.调用InputManager.cpp的registerInputChannel接口
    status_t status = im->registerInputChannel(env, inputChannel);
    ...
}

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel) {
    ATRACE_CALL();
    // 调用InputDispatcher.cpp的registerInputChannel实现注册逻辑
    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
    ...
    { // acquire lock
        std::scoped_lock _l(mLock);
        // 如果已经存在Connection,则不必重复注册
        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
        if (existingConnection != nullptr) {
            ...
            return BAD_VALUE;
        }
        // 1.封装创建Connection连接,将服务端inputChannel传进去
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
        // 读取server服务端inputChannel的socket的fd
        int fd = inputChannel->getFd();

        // 2.以socket的fd为key,connection为value,保存到map mConnectionsByFd中
        mConnectionsByFd[fd] = connection;

        // 3.以token为key,inputChannel为value,保存在map mInputChannelsByToken中
        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;

        // 4.将服务端的socket的fd添加到looper监听,待客户端发送事件后会触发回调handleReceiveCallback函数
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

这块核心的逻辑其实就是:

1.判断如果是首次注册InputChannel,则会封装创建一个Connection连接,并以InputChannel的socket的fd为key,此连接为value,存储到InputDispatcher的mConnectionsByFd中,InputChannel的token对象则保证了注册InputChannel的窗口的唯一性;token为key,InputChannel为value保存在了InputDispatcher的mInputChannelsByToken中

2.将server端socket的fd添加到InputDispatcher内部的looper进行监听,待应用APP那边的client客户端socket写入了数据时触发回调其handleReceiveCallback函数进行处理。

(重要)让我们回忆结合5.1节中的分析触控事件发送到目标窗口的逻辑,在InputDispatcher::dispatchEventLocked函数中第一步就是需要遍历目标窗口,然后根据找到的目标窗口的token就能直接取出对应的Connection连接,其实就是因为这些目标窗口在创建时就已经完成了上述注册过程,并已经添加相关信息到mConnectionsByFd和mInputChannelsByToken集合中了,所以在事件分发时就能根据唯一的toke身份标识从集合中直接取出,至此完成前后逻辑呼应。

5.2.3 App客户端监听触控事件

我们回到WindowState::openInputChannel中继续往下看,服务端InputChannel注册完成之后,mClientChannel.transferTo(outInputChannel)会将客户端InputChannel赋值给outInputChannel,并Binder回传给App应用进程的ViewRootImpl中。这之后App应用进程需要根据换船过来的客户端的InputChannel完成注册监听触控事件的逻辑,以便后续框架InputDispatcher的触控事件传递过来之后,应用能及时响应。我们先用一张流程图看一下这个过程:

App触控事件监听的逻辑.png

下面回到应用客户端ViewRootImpl的setView中结合代码分析一下这个过程:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/   
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                ...
                InputChannel inputChannel = null;
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    // 1.创建一个空的InputChannel
                    inputChannel = new InputChannel();
                }
                try {
                    ...
                    // 2.Binder调用WMS的addToDisplayAsUser接口,并将InputChanel作为参数传入,以完成赋值
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                } catch (RemoteException e) {
                    ...
                }
                ...
                if (inputChannel != null) {
                    ...
                   // 3.根据赋值后的InputChannel封装创建WindowInputEventReceiver对象,以便后续接收input事件
                    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());
                }
                ...
            }
        }
    }

通过WMS的addToDisplayAsUser给空的客户端InputChannel的赋值后,创建一个WindowInputEventReceiver对象,其构造方法接收客户端的InputChannel和应用UI主线程的Looper对象

  /*frameworks/base/core/java/android/view/ViewRootImpl.java*/  
  final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            // 调用父类InputEventReceiver的构造函数
            super(inputChannel, looper);
        }
  }

  /*frameworks/base/core/java/android/view/InputEventReceiver.java*/  
  public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        ...
        // 通过JNI调用nativeInit完成初始化
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
        ...
  }

  /*frameworks/base/core/jni/android_view_InputEventReceiver.cpp*/  
  static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    // 1.java层InputChannel转换得到native层InputChannel,client端
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    ...
    // 2.APP UI线程对应native层的MessageQueue
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...
    // 3.创建NativeInputEventReceiver,将inputChannel,messageQueue保存到其内部成员变量
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    ...
}

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        // 1.mInputConsumer就是保存到其内部的client端InputChannel,fd指向client端InputChannel内部的client端socket的fd
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            // 2.将client端socket的fd添加到Looper进行监听,监听事件类型为ALOOPER_EVENT_INPUT
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}    

这块核心的逻辑其实就是:取出client端InputChannel的socket的fd,添加到APP进程的UI线程的Looper进行监听,且监听事件类型为ALOOPER_EVENT_INPUT,并在接收到事件之后(server端socket有写入数据时,也就是前面分析的InputChannel::sendMessage中InputDispatcher向目标窗口发送触控事件的send动作)在UI线程回调NativeInputEventReceiver::handleEvent函数进行处理。最后来看看NativeInputEventReceiver::handleEvent中的处理:

/*frameworks/base/core/jni/android_view_InputEventReceiver.cpp*/  
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ...
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        // 判断ALOOPER_EVENT_INPUT事件后调用consumeEvents消费事件
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    ...
    return 1;
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...
    for (;;) {
        ...
        InputEvent* inputEvent;
        // 1.读取获取input事件
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent,
                &motionEventType, &touchMoveNum, &flag);
        ...
        if (!skipCallbacks) {
            ...
            if (inputEventObj) {
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
                }
               // 2.JNI回调java层的InputDispatcherReceiver中的dispatchInputEvent函数
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                ...
            } else {
                ...
            }
        }
		...
    }
}

 /*frameworks/base/core/java/android/view/InputEventReceiver.java*/  
  // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        // 调用onInputEvent具体处理input event触控事件
        onInputEvent(event);
    }

对于APP UI线程,在接收到server端socket的消息时会回调InputEventReceiver对应的native层对象NativeInputEventReceiver的handleEvent函数。然后会JNI通知回调到应用App的java层的ViewRootImpl::WindowInputDispatcherReceiver::onInputEvent函数进一步处理,之后的逻辑就是一下节中需要具体分析的App应用目标窗口内部的事件传递了

6 目标窗口内部的事件传递机制

到了这里就进入了App应用开发者最熟悉也是最关心的部分了,input触控事件已经进入了App应用进程这边且进入了Java层的世界了。在本节中我们将接上一节的分析,从InputEventReceiver::onInputEvent函数入手,分两个小节分析Input触控事件在应用目标窗口内的传递流程。

6.1 UI线程对触控事件的分发处理

从ViewRootImpl::WindowInputDispatcherReceiver::onInputEvent函数开始,UI线程对触控事件的处理流程如下图所示:

UI线程对触控事件的分发处理.png

从上面的流程图中我们可以看到:Input事件到来后先通过enqueueInputEvent函数放入“aq”本地待处理队列中,简化代码如下:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
  @UnsupportedAppUsage
    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        // 构造一个QueuedInputEvent对象,使用obtainQueuedInputEvent构造对象,通过对象池的形式保存回收对象,内部使用链表数据结构
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        // QueuedInputEvent数量加1,更新相关trace Tag的打印,从systrace上看tag信息为“aq:pending:XXX”,最后一节中详细分析
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        if (processImmediately) {
            // doProcessInputEvents具体处理此次input事件
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

具体对input触控事件的处理逻辑都封装在InputUsage中,在ViewRootImpl创建Window时的setView逻辑中会创建了多个不同类型的InputUsage对象依次进行事件处理,设计上采用责任链模式,将每个InputStage实现类通过mNext变量连接起来。每个InputUsage主要逻辑是在OnProcess函数中具体处理触控事件,然后判断处理是否完成,没有则onDeliverToNext交给下一个InputUsage继续处理,否则就调用finishInputEvent结束事件。代码流程如下:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
            ...
                // Set up the input pipeline.
                CharSequence counterSuffix = attrs.getTitle();
                // 创建多个不同类型InputUsage以处理不同类型的触控事件
                mSyntheticInputStage = new SyntheticInputStage();
                // ViewPostImeInputStage中封装我们应用一般触控事件处理逻辑(重要)
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                ...
    }

abstract class InputStage {
        ...
        /**
         * Delivers an event to be processed.
         */
        public final void deliver(QueuedInputEvent q) {
            // 是否带有FLAG_FINISHED的flag,如有则将事件分发给mNext
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
             // 是否应该丢弃事件
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                traceEvent(q, Trace.TRACE_TAG_VIEW);
                final int result;
                try {
                    // 1.实际处理事件
                    result = onProcess(q);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
                // 2.拿到处理事件的结果后决定进一步行动
                apply(q, result);
            }
        }

        protected void apply(QueuedInputEvent q, int result) {
            if (result == FORWARD) {
                forward(q);
            } else if (result == FINISH_HANDLED) {
                finish(q, true);
            } else if (result == FINISH_NOT_HANDLED) {
                finish(q, false);
            }
        }

        protected void finish(QueuedInputEvent q, boolean handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
            if (handled) {
                q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
            }
            // 交给下一个InputUsage继续处理
            forward(q);
        }

        protected void forward(QueuedInputEvent q) {
            // 交给下一个InputUsage继续处理
            onDeliverToNext(q);
        }

        protected void onDeliverToNext(QueuedInputEvent q) {
            if (DEBUG_INPUT_STAGES) {
                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
            }
            if (mNext != null) {
               // 1.交给下一个InputUsage继续处理
                mNext.deliver(q);
            } else {
                // 2.判断所有的InputUsage都处理完成则调用finishInputEvent结束触控事件
                finishInputEvent(q);
            }
        }
}

注意最后触控事件处理完成后会调用finishInputEvent结束应用对触控事件处理逻辑,这里面会通过JNI调用到native层InputConsumer的sendFinishedSignal函数,最终还是通过client端InputChannel的sendMessage通知InputDispatcher事件已经处理完成,而作为server端的InputDispatcher这边的主要就是在收到消息后将此触控事件从waitQueue等待队列中移除并重置ANR时间(所以我们开发应用过程中,如果UI线程存在长时间的耗时或阻塞的操作,导致主线程不能及时处理完触控事件逻辑而向框架InputDispatcher回传finishInputEvent的消息,最终出现waitQueue中计时超时而出现ANR问题)

6.2 View的触控事件分发机制

接着上一节的分析中我们知道:触控事件首先分发到View树的根节点DecorView的dispatchTouchEvent函数中,这也是应用窗口界面布局View树的触控事件处理的入口位置,这一节也是App应用开发者最熟悉和关心的部分,我们看看它的具体代码实现:

/*frameworks/base/core/java/com/android/internal/policy/DecorView.java*/
 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        // 交给应用Activity的dispatchTouchEvent处理触控事件
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

其中Window.Callback指向当前Activity,在Activity启动时会调用自己的attach方法,此方法中会将自己作为callback传给window,继续看Activity的dispatchTouchEvent处理逻辑:

/*frameworks/base/core/java/android/app/Activity.java*/
public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        // 先交给窗口PhoneWindow处理
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        // PhoneWindow没有消费事件则继续交给Activity的onTouchEvent继续处理
        return onTouchEvent(ev);
}

Activity中会先将事件交给PhoneWindow处理,实际上就是交给DecorView中处理,逻辑如下:

/*frameworks/base/core/java/com/android/internal/policy/DecorView.java*/
    public boolean superDispatchTouchEvent(MotionEvent event) {
        // 具体交给其父类ViewGroup处理
        return super.dispatchTouchEvent(event);
    }

继续看ViewGroup中的处理:

 /*frameworks/base/core/java/android/ViewGroup.java*/
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        if (onFilterTouchEventForSecurity(ev)) {
            ...
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                // 1.判断子View中是否设置了禁止父ViewGroup拦截触控事件,解决滑动冲突的关键
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    // onInterceptTouchEvent中判断ViewGroup是否拦截触控事件
                    intercepted = onInterceptTouchEvent(ev);
                }
            }
            ...
            if (!canceled && !intercepted) {
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    ...
                    if (newTouchTarget == null && childrenCount != 0) {
                        ...
                        final View[] children = mChildren;
                        // 2.对所有子View进行遍历
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            ...
                            if (!child.canReceivePointerEvents()
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                               // 3.如果此View无法接收事件或者当前事件的或落点不在这个View区域内则返回进行下一轮循环
                                continue;
                            }
                            ...
                            // 4.这里就会执行子View事件分发处理逻辑了
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                ...
                                break;
                            }
                            ...
                        }
                    }
                    ...
                }
            }
        ...
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        // ViewGroup默认不拦截事件
        return false;
    }

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        ...
        // Perform any necessary transformations and dispatch.
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            ...
            // 调用子View的dispatchTouchEvent继续进行事件分发
            handled = child.dispatchTouchEvent(transformedEvent);
        }
        ...
    }

  • 分发的主要逻辑就是:判断子View中是否设置了禁止父ViewGroup拦截触控事件(解决滑动冲突的一个主要思路就是子View通过调用requestDisallowInterceptTouchEvent禁止父ViewGroup拦截触控事件,从而将事件统一交给子View处理以避免产生滑动冲突),如果没有则onInterceptTouchEvent判断是否拦截此事件;如果不拦截,则遍历所有子View找到匹配的子View,然后交给该子View的dispatchTouchEvent继续处理。

我们继续往下看View中对触控事件的处理逻辑:

 /*frameworks/base/core/java/android/View.java*/
    public boolean dispatchTouchEvent(MotionEvent event) {
        ...
        boolean result = false;
        if (onFilterTouchEventForSecurity(event)) {
            ...
            // 1.判断是否调用过setOnTouchListener方法,如果有则将事件传入其onTouch方法进行处理并返回结果
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            // 2.onTouchEvent中判断是否消费触控事件
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ...
        return result;
    }

首先判断该View是否设置了触摸监听,即是否调用过setOnTouchListener方法,如果有则将事件传入其onTouch方法进行处理并返回结果,如果没有设置,则会调用View的onTouchEvent方法,此方法中会有条件的调用onClick,对于事件处理的顺序就是:onTouch,onTouchEvent,onClick

总体上View对触控事件的处理逻辑如下图所示:

View对触控事件的分发机制.png

7 触控问题调试机制

根据笔者多年的经验总结一些常见的系统触控调试机制:

1、查看支持的触控输入设备节点

input.PNG

2、adb getevent 命令查看屏幕报点情况: adb shell getevent -ltr 可以滑动查看屏幕触控报点是否正常和均匀: getevent.PNG

3、显示触控小白点

设置->开发者选项->显示触摸操作

开启后触摸屏幕能看到小白点实时显示,能够主观感受屏幕滑动跟手度等状况。

4、Systrace上查看触控事件分发的全流程

从前面的分析可以知道:InputDispatcher进行事件分发是会有一些处理队列,源码都加了一些trace tag的的打印和计数,如InputReader读取到触控事件后唤醒InputDispatcher会放入“iq”队列中,然后进行事件分发时每个目标窗口都有对应的队列“oq”和等待目标窗口事件处理的“wq”队列,最后应用这边收到触控事件后还有对应的“aq”队列,从systrace上看如下图所示: system_server进程的InputDispatcher: input调试之systrace.PNG

目标窗口应用App进程: input调试之systrace_aq.PNG

应用UI线程处理的systrace tag: systrace_input.PNG

5、adb shell dumpsys input 通过dump查看触控事件处理系统框架部分:EventHub、InputReader、InputDispatcher的工作状态:

dump_input.PNG

8 总结

最后借用业界大牛做的一张图来描述Android系统触控事件处理机制的整体全貌作为总结。 input模型.PNG

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值