Input体系: InputReader 处理接触事情 剖析了 InputReader 对接触事情的处理流程,终究的成果是把接触事情包装成 NotifyMotionArgs,然后分发给下一环。依据 Input体系: InputManagerService的创立与发动 可知,下一环是 InputClassifier。然而体系目前并不支持 InputClassifier 的功用,因而事情会被直接发送到 InputDispatcher

Input体系: 按键事情分发 剖析了按键事情的分发流程,尽管剖析的目标是按键事情,可是也从全体上,描绘了事情分发的结构。而本文剖析接触事情的分发流程,也会用到这个结构,因而重复内容不再赘述。

1. InputDispatcher 收到接触事情

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
                             args->pointerProperties)) {
        return;
    }
    uint32_t policyFlags = args->policyFlags;
    // 来自InputReader/InputClassifier的 motion 事情,都是受信赖的
    policyFlags |= POLICY_FLAG_TRUSTED;
    android::base::Timer t;
    // 1. 对接触事情履行切断战略
    // 接触事情入队前,查询切断战略,查询的成果保存到参数 policyFlags
    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 lock
        mLock.lock();
        if (shouldSendMotionToInputFilterLocked(args)) {
            // ...
        }
        // 包装成 MotionEntry
        // Just enqueue a new motion event.
        std::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);
        // 2. 把接触事情参加收件箱
        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock
    // 3. 假如有必要,唤醒线程处理接触事情
    if (needWake) {
        mLooper->wake();
    }
}

InputDispatcher 收到接触事情后的处理流程,与收到按键事情的处理流程非常相似

  1. 对接触事情进行切断战略查询。参阅【1.1 切断战略查询
  2. 把接触事情参加 InputDispatcher 收件箱,然后唤醒线程处理接触事情。

1.1 切断战略查询

void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
        uint32_t& policyFlags) {
    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();
            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;
        }
    }
}
void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    }
}

一个接触事情,必须满意下面三种状况,才履行切断战略

  1. 接触事情是受信赖的。来自输入设备的接触事情都是受信赖的。
  2. 接触事情对错注入的。monkey 的原理便是注入接触事情,因而它的事情是不需求经过切断战略处理的。
  3. 设备处于非交互状况。一般来说,非交互状况指的便是显现屏处于灭屏状况。

别的还需求关注的是,事情在什么时分是不需求经过切断战略,有两种状况

  1. 关于受信赖且非注入的接触事情,假如设备处于交互状况,直接发送给用户。 也便是说,假如显现屏处于亮屏状况,输入设备产生的接触事情必定会发送给窗口。
  2. 关于不受信赖,或许注入的接触事情,假如设备处于交互状况,也是直接发送给用户。也便是说,假如显现屏处于亮屏状况,monkey 注入的接触事情,也是直接发送给窗口的。

终究还要留意一件事,假如一个接触事情是不受信赖的事情,或许是注入事情,当设备处于非交互状况下(一般指灭屏),那么它不经过切断战略,也不会发送给用户,也便是会被丢掉。

在实践工作中处理的接触事情,一般都是来自输入设备,它肯定是受信赖的,而且非注入的,因而它只需在设备处于非交互状况下(一般指灭屏)下,非会履行切断战略,而假如设备处于交互状况(一般指亮屏),会被直接分发给窗口。

现在来看下切断战略的详细完成

// PhoneWindowManager.java
    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
            int policyFlags) {
        // 1. 假如战略要求唤醒屏幕,那么切断这个接触事情
        // 一般来说,唤醒屏幕的战略取决于设备的配置文件
        if ((policyFlags & FLAG_WAKE) != 0) {
            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
                // 回来 0,标明切断接触事情
                return 0;
            }
        }
        // 2. 判别非交互状况下,是否切断事情
        if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
            // 回来这个值,标明不切断事情,也便是事情分发给用户
            return ACTION_PASS_TO_USER;
        }
        // 疏忽 theater mode
        if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
        }
        // 3. 默许切断接触事情
        // 回来0,标明切断事情
        return 0;
    }
    private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
        // Apply the default display policy to unknown displays as well.
        final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
                || displayId == INVALID_DISPLAY;
        final Display display = isDefaultDisplay
                ? mDefaultDisplay
                : mDisplayManager.getDisplay(displayId);
        final boolean displayOff = (display == null
                || display.getState() == STATE_OFF);
        if (displayOff && !mHasFeatureWatch) {
            return false;
        }
        // displayOff 标明屏幕处于 off 状况,可对错 off 状况,并不标明必定是亮屏状况
        // 关于 doze 状况,屏幕处于 on 状况,可是屏幕或许仍然是黑的
        // 因而,只需屏幕处于 on 状况,而且显现了锁屏,接触事情不会切断
        if (isKeyguardShowingAndNotOccluded() && !displayOff) {
            return true;
        }
        // 关于接触事情,keyCode 的值为 KEYCODE_UNKNOWN
        if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK
                || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY
                || keyCode == KeyEvent.KEYCODE_STEM_1
                || keyCode == KeyEvent.KEYCODE_STEM_2
                || keyCode == KeyEvent.KEYCODE_STEM_3)) {
            return false;
        }
        // 关于默许屏幕,假如设备处于梦境状况,那么接触事情不切断
        // 因为 doze 组件需求接纳接触事情,或许会唤醒屏幕
        if (isDefaultDisplay) {
            IDreamManager dreamManager = getDreamManager();
            try {
                if (dreamManager != null && dreamManager.isDreaming()) {
                    return true;
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "RemoteException when checking if dreaming", e);
            }
        }
        // Otherwise, consume events since the user can't see what is being
        // interacted with.
        return false;
    }    

切断战略是否切断接触事情,取决于战略的回来值,有两种状况

  1. 回来 0,标明切断接触事情。
  2. 回来 ACTION_PASS_TO_USER ,标明不切断接触事情,也便是把接触事情分发给用户/窗口。

下面罗列接触事情切断与否的状况,可是要留意一个前提,设备处于非交互状况(一般便是指灭屏状况)

  1. 事情会被传递给用户,也便是不切断,状况如下
    • 有锁屏,而且显现屏处于非 off 状况。留意,非 off 状况,并不是标明屏幕处于 on(亮屏) 状况,也或许是 doze 状况(屏幕处于低电量状况),doze 状况屏幕也是黑的。
    • 梦境状况。因为梦境状况下会运转 doze 组件。
  2. 事情被切断,状况如下
    • 战略标志位包括 FLAG_WAKE ,它会导致屏幕被唤醒,因而需求切断接触事情。FLAG_WAKE 一般来自于输入设备的配置文件。
    • 没有锁屏,没有梦境,也没有 FLAG_WAKE,默许就会切断。

从上面的剖析能够总结出了两条定论

  1. 假如体系有组件在运转,例如,锁屏、doze组件,那么接触事情需求分发到这些组件,因而不会被切断。
  2. 假如没有组件运转,接触事情都会被切断。接触事情因为需求唤醒屏幕,而导致被切断,只是其中一个特例。

2. InputDispatcher 分发接触事情

由 Input体系: InputManagerService的创立与发动 可知,InputDispatcher 经过线程循环来处理收件箱中的事情,而且一次循环只能处理一个事情

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();
        if (!haveCommandsLocked()) {
            // 1. 分发一个接触事情
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        // 接触事情的分发进程不会产生指令
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
        // 2. 核算线程下次唤醒的时刻点,以便处理 anr
        const nsecs_t nextAnrCheck = processAnrsLocked();
        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
        if (nextWakeupTime == LONG_LONG_MAX) {
            mDispatcherEnteredIdle.notify_all();
        }
    } // release lock
    // 3. 线程休眠指定的时长
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

一次线程循环处理接触事情的进程如下

  1. 分发一个接触事情。
  2. 当事情分发给窗口后,会核算一个窗口反应的超时时刻,使用这个时刻,核算线程下次唤醒的时刻点。
  3. 使用上一步核算出的线程唤醒的时刻点,核算出线程终究需求休眠多长时刻。当线程被唤醒后,会查看接纳接触时刻的窗口,是否反应超时,假如超时,会引发 ANR。

现在来看看怎么分发一个接触事情

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }
    if (mDispatchFrozen) {
        return;
    }
    // 这儿是优化 app 切换的延迟
    // mAppSwitchDueTime 是 app 切换的超时时刻,假如小于当时时刻,那么标明app切换超时了
    // 假如app切换超时,那么在app切换按键事情之前的未处理的事情,都将会被丢掉
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }
    // mPendingEvent 标明正在处理的事情
    if (!mPendingEvent) {
        if (mInboundQueue.empty()) {
            // ...
        } else {
            // 1. 从收件箱行列中取出事情
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }
        // 假如这个事情需求传递给用户,那么需求同上层的 PowerManagerService,此时有用户行为,这个效果便是延伸亮屏的时刻
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(*mPendingEvent);
        }
    }
    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;
            }
            // 2. 分发接触事情
            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
            break;
        }
        // ...
    }
    // 3. 假如事情被处理,重置一些状况,例如 mPendingEvent
    // 回来 true,就标明现已处理了事情
    // 事情被丢掉,或许发送完毕,都会回来 true
    // 回来 false,标明暂时不知道怎么处理事情,因而线程会休眠
    // 然后,线程再次被唤醒时,再来处理这个事情
    if (done) {
        if (dropReason != DropReason::NOT_DROPPED) {
            dropInboundEventLocked(*mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;
        // 重置 mPendingEvent
        releasePendingEventLocked();
        // 立即唤醒,处理下一个事情
        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
    }
}

Input体系: 按键事情分发 现已剖析过 InputDispatcher 的线程循环。而关于接触事情,是经过 InputDispatcher::dispatchMotionLocked() 进行分发

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
    if (!entry->dispatchInProgress) {
        entry->dispatchInProgress = true;
    }
    // 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;
    std::vector<InputTarget> inputTargets;
    bool conflictingPointerActions = false;
    InputEventInjectionResult injectionResult;
    if (isPointerEvent) {
        // 寻觅接触的窗口,窗口保存到 inputTargets
        // 2. 为接触事情,寻觅接触的窗口
        // 接触的窗口保存到 inputTargets 中
        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions);
    } else {
        // ...
    }
    if (injectionResult == InputEventInjectionResult::PENDING) {
        // 回来 false,标明暂时不知道怎么处理这个事情,这会导致线程休眠
        // 等线程下次被唤醒时,再来处理这个事情
        return false;
    }
    // 走到这儿,标明接触事情现已被处理,因而保存处理的成果
    // 只需回来的不是 InputEventInjectionResult::PENDING
    // 都标明事情被处理,无论是权限拒绝还是失利,或是成功
    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.
    // 3. 接触事情找到了接触窗口,在分发给窗口前,保存 global monitor 到 inputTargets 中
    // 开发者选项中的 Show taps 和 Pointer location,使用的 global monitor
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
    if (isPointerEvent) {
        // ... 省掉 portal window 处理的代码
    }
    if (conflictingPointerActions) {
        // ...
    }
    // 4. 分发事情给 inputTargets 中的一切窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

一个接触事情的分发进程,能够大致总结为以下几个进程

  1. 假如有原因标明接触事情需求被丢掉,那么接触事情不会走后边的分发流程,即被丢掉。
  2. 一般接触事情是发送给窗口的,因而需求为接触事情寻觅接触窗口。窗口终究被保存到 inputTargets 中。参阅【2.1 寻觅接触的窗口
  3. inputTargets 保存接触窗口后,还要保存 global monitor 窗口。例如开发者选项中的 Show taps 和 Pointer location,便是使用这个窗口完成的。
  4. 发动分发循环,把接触事情分发给 inputTargets 保存的窗口。 因为 Input体系: 按键事情分发 现已分发过这个进程,本文不再剖析。

2.1 寻觅接触的窗口

InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
        nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
    // ...
    // 6. 关于非 DOWN 事情,获取现已 DOWN 事情保存的 TouchState
    // TouchState 保存了接纳 DOWN 事情的窗口
    const TouchState* oldState = nullptr;
    TouchState tempTouchState;
    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
            mTouchStatesByDisplay.find(displayId);
    if (oldStateIt != mTouchStatesByDisplay.end()) {
        oldState = &(oldStateIt->second);
        tempTouchState.copyFrom(*oldState);
    }
    // ...
    // 第一个条件 newGesture 标明第一个手指按下
    // 后边一个条件,标明当时窗口支持 split motion,而且此时有别的一个手指按下
    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
        // 接触点的获取 x, y 坐标
        int32_t x;
        int32_t y;
        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        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));
        }
        // 这儿检测是否是第一个手指按下
        bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        // 1. 关于 DOWN 事情,依据接触事情的x,y坐标,寻觅接触窗口
        // 参数 addOutsideTargets 标明,只需在第一个手指按下时,假如没有找到接触的窗口,
        // 那么需求保存那些能够接受 OUTSIZE 事情的窗口到 tempTouchState
        newTouchedWindowHandle =
                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
        // 省掉 ... 处理窗口反常的状况 ...
        // 2. 获取一切的 getsture monitor
        const std::vector<TouchedMonitor> newGestureMonitors = isDown
                ? selectResponsiveMonitorsLocked(
                          findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows))
                : tempTouchState.gestureMonitors;
        // 既没有找到接触点地点的窗口,也没有找到 gesture monitor,那么此次寻觅接触窗口的任务就失利了
        if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
            ALOGI("Dropping event because there is no touchable window or gesture monitor at "
                  "(%d, %d) in display %" PRId32 ".",
                  x, y, displayId);
            injectionResult = InputEventInjectionResult::FAILED;
            goto Failed;
        }
        // 走到这儿,标明找到了接触的窗口,或许找到 gesture monitor
        if (newTouchedWindowHandle != nullptr) {
            // 立刻要保存窗口了,现在获取窗口的 flag
            int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
            if (isSplit) {
                targetFlags |= InputTarget::FLAG_SPLIT;
            }
            if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
                targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
            } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
                targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
            }
            // Update hover state.
            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                newHoverWindowHandle = nullptr;
            } else if (isHoverAction) {
                newHoverWindowHandle = newTouchedWindowHandle;
            }
            // Update the temporary touch state.
            // 假如窗口支持 split,那么用 tempTouchState 保存窗口的时分,要特别保存 pointer id
            BitSet32 pointerIds;
            if (isSplit) {
                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                pointerIds.markBit(pointerId);
            }
            // 3. tempTouchState 保存找到的接触的窗口
            // 假如是真的找到的接触窗口,那么这儿便是保存,假如是找到能够接受 OUTSIDE 的窗口,那么这儿是更新
            tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
        } else if (tempTouchState.windows.empty()) {
            // If no window is touched, set split to true. This will allow the next pointer down to
            // be delivered to a new window which supports split touch.
            tempTouchState.split = true;
        }
        if (isDown) {
            // tempTouchState 保存一切的 gesture monitor
            // 4. 第一个手指按下时,tempTouchState 保存 gesture monitor
            tempTouchState.addGestureMonitors(newGestureMonitors);
        }
    } else {
        // ...
    }
    if (newHoverWindowHandle != mLastHoverWindowHandle) {
        // ....
    }
    {
        // 权限检测 ...
    }
    // 保存接纳 AMOTION_EVENT_ACTION_OUTSIDE 的窗口
    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
        // ...
    }
    // 第一个手指按下时,保存壁纸窗口
    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // 
        // ...
    }
    // 走到这儿,标明没有反常状况了
    injectionResult = InputEventInjectionResult::SUCCEEDED;
    // 5. 把 tempTouchState 保存了接触窗口和gesture monitor,保存到 inputTargets 中
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, inputTargets);
    }
    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
        addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                                  touchedMonitor.yOffset, inputTargets);
    }
    // Drop the outside or hover touch windows since we will not care about them
    // in the next iteration.
    tempTouchState.filterNonAsIsTouchWindows();
Failed:
    // ...
    // 6. 缓存 tempTouchState
    if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
        if (tempTouchState.displayId >= 0) {
            mTouchStatesByDisplay[displayId] = tempTouchState;
        } else {
            mTouchStatesByDisplay.erase(displayId);
        }
    } 
    return injectionResult;
}

为接触事情寻觅接触窗口的进程,极端复杂。尽管这段代码被我省掉了许多进程,可是我估量读者也会看得头晕。

关于 DOWN 事情

  1. 依据 x,y 坐标寻觅接触的窗口。参阅【2.1.1 依据坐标找到接触窗口
  2. 获取一切的 gesture monitor 窗口 。
  3. 把接触窗口保存到 tempTouchState 中。
  4. 把一切的 gesture monitor 窗口保存到 tempTouchState 中。
  5. 为 tempTouchState 保存一切窗口,创立 InputTarget 对象,并保存到参数 inputTargets 中。参阅【2.1.2 保存窗口
  6. 使用 mTouchStatesByDisplay 缓存 tempTouchState。

gesture monitor 是为了完成手势功用而增加的一个窗口。什么是手势功用? 例如在屏幕的左面/右边,向屏幕中央滑动,会触发回来手势。这个手势功用用来替代导航键。在下一篇文章中,我会剖析这个手势功用的原理。

关于非 DOWN 事情,一般为 MOVE, UP 事情

  1. 获取 DOWN 事情缓存的 tempTouchState。 因为 tempTouchState 保存了处理 DOWN 事情的接触窗口和 gesture monitor,非 DOWN 事情,也会发送给这些窗口。
  2. 重复 DOWN 事情的第5步。

当剖析的代码量很大的时分,我们需求有一个全体的观念。为接触事情寻觅接触窗口,终究的成果便是把找到的窗口保存到参数 inputTargets 中,后边会把事情分发给 inputTargets 保存的窗口。

2.1.1 依据坐标找到接触窗口

// addOutsideTargets 在第一个手指按下是为 true
// addPortalWindows 值为 true
// ignoreDragWindow 默许为 false
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
                                                                 bool addOutsideTargets,
                                                                 bool addPortalWindows,
                                                                 bool ignoreDragWindow) {
    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
        LOG_ALWAYS_FATAL(
                "Must provide a valid touch state if adding portal windows or outside targets");
    }
    // Traverse windows from front to back to find touched window.
    // 从前到后,遍历窗口
    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        // ignoreDragWindow 默许为 false
        if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
            continue;
        }
        // 获取窗口信息
        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)) {
                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                        // 假如是 portal window
                        if (portalToDisplayId != ADISPLAY_ID_NONE &&
                            portalToDisplayId != displayId) {
                            if (addPortalWindows) {
                                // For the monitoring channels of the display.
                                // touchState 保存 portal window
                                touchState->addPortalWindow(windowHandle);
                            }
                            // 递归调用,获取 portal display id 下的接触窗口
                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
                                                             addOutsideTargets, addPortalWindows);
                        }
                        // 不是 portal window,直接回来找到的窗口
                        return windowHandle;
                    }
                }
                // 走到这儿,标明没有找到接触窗口。也便是说,既没有找到接触模型的窗口,也没有找到包括接触点的窗口
                // 当第一个手指按下是,addOutsideTargets 值为 true
                // NOT_TOUCH_MODAL 和 WATCH_OUTSIDE_TOUCH 一同使用,当第一个手指按下时,假如落在窗口之外
                // 窗口会收到 MotionEvent.ACTION_OUTSIDE 事情
                if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
                    touchState->addOrUpdateWindow(windowHandle,
                                                  InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                                  BitSet32(0));
                }
            }
        }
    }
    return nullptr;
}

这儿触及一个 portal window 的概念,因为我没有找到详细使用的当地,我大致猜测它的意思便是,设备外接一个屏幕,然后在主屏幕上显现一个窗口来操作这个外接屏幕。后边的剖析,我将略过 portal window 的部分。当然,接触掌握了接触事情的分发流程,今后遇到了 portal window 的事情,再来剖析,应该没问题的。

寻觅接触点地点的窗口,其实便是从上到下遍历一切窗口,然后找到满意条件的窗口。

窗口首先要满意前置条件

  1. 窗口要在指定屏幕上。
  2. 窗口要可见。
  3. 窗口要可接触。

满意了一切的前置条件后,只需满意以下任意一个条件,那么就找到了接触点地点的窗口

  1. 是接触模型的窗口: 可获取焦点,而且不允许窗口之外的接触事情发送到它后边的窗口。
  2. 接触点的 x,y 坐标落在窗口坐标系中。

2.1.2 保存窗口

// InputDispatcher 保存接触窗口
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();
    // 创立 InputTarget,并保存到参数 inputTargets
    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 =
                int2(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);
    // 保存 InputTarget 后,在保存窗口的坐标转化参数,
    // 这个参数能够把显现屏的坐标,转化为窗口的坐标
    it->addPointers(pointerIds, windowInfo->transform);
}
// InputDispatcher 保存 gesture monitor
void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
                                                float yOffset,
                                                std::vector<InputTarget>& inputTargets) {
    InputTarget target;
    target.inputChannel = monitor.inputChannel;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
    ui::Transform t;
    t.set(xOffset, yOffset);
    target.setDefaultPointerTransform(t);
    inputTargets.push_back(target);
}

关于接触事情,无论是接触窗口,还是 gesture monitor,都会被转化为 InputTarget,然后保存到参数 inputTargets 中。当后边发动分发循环后,接触事情就会发送到 inputTargets 保存的窗口中。

完毕

本文从全体上剖析了接触事情的分发进程,许多细节并没有深入去剖析,例如,当窗口无呼应时,怎么优化事情分发。可是,只需你掌握了基本的流程,这些细节你能够自行剖析。

本文的某些剖析进程,跨度或许很大,那是因为这些知识现已在前面的文章中讲过,假如你阅览本文,感觉有点困难,那么请先阅览前面的文章,打好根底。

理论的文章总有一些枯燥,可是不阻碍我继续向前,下一篇文章,将以此为根底,剖析那个替代体系导航栏的手势功用是怎么完成的,这也将作为 Input 体系的收官之作。