InputSystem發送輸入事件到應用的過程

基于Android 7.0源碼分析

以最基本的MotionEvent(touchsreen single pointer)為例分析

InputReader讀取輸入事件分發給InputDispatcher的過程

下面從InputReaderloopOnce()開始分析

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    ......
    // 獲取驅動上報的輸入事件,返回讀取的事件數
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            // 處理輸入事件
            processEventsLocked(mEventBuffer, count);
        }
        ......
    }
    ......
    // 發送事件給InputDispatcher
    mQueuedListener->flush();
}

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        ......
        // Grab the next input event.
        // 讀取輸入事件
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            ......
            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }
            // 找到上報事件的設備
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                // 讀取輸入事件
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    // 讀取失敗,設備已經移除
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                            " bufferSize: %zu capacity: %zu errno: %d)\n",
                            device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    // 輸入事件不完整
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        // 解析讀取的事件,封裝為RawEvent
                        struct input_event& iev = readBuffer[i];
                        ......
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                }
            }
        }
        ......
        // 讀取到一些事件立即返回
        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }
    }
    ......
    // All done, return the number of events we read.
    return event - buffer;
}
  • RawEvent中的時間使用的是驅動上報事件的時間,更加精確;另外還要防止輸入設備時鐘源切換引入的問題

下面分析processEventsLocked()的實現

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            // 統計同一設備普通輸入事件
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            ......
            // 處理普通的輸入事件,同一設備的事件一起處理
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            ......
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }
    // 找到對應的設備
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    // 調用InputDevice的process處理
    device->process(rawEvents, count);
}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
            ......
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                // 對于MotionEvent, 由MultiTouchInputMapper處理
                mapper->process(rawEvent);
            }
            ......
    }
}

在分析MultiTouchInputMapperprocess()實現之前,先簡單了解下touchscreen驅動上報的原始輸入事件的格式。

Down/Move事件類似:
 1) report (EV_ABS, ABS_MT_SLOT) // 存放事件的SLOT
 2) report (EV_ABS, ABS_MT_TRACKING_ID(非負)) // touch軌跡id
 3) report (EV_ABS, ABS_MT_TOOL_TYPE = 0) // touch設備類型
 4) report (EV_ABS, ABS_MT_POSITION_X(x坐標)) // touch區域(橢圓)的中心點的X坐標
 5) report (EV_ABS, ABS_MT_POSITION_Y(Y坐標)) // touch區域(橢圓)的中心點的Y坐標
 6) report (EV_SYN, SYN_REPORT) // 完整事件標示

Up事件:
 1) report (EV_ABS, ABS_MT_SLOT)
 2) report (EV_ABS, ABS_MT_TRACKING_ID(-1))
 3) report (EV_SYN, SYN_REPORT)

更多信息請參考kernel multi-touch-protocol.txt文檔
  • 不同的touchscreen驅動會有所差異

下面看process()實現

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    // 原始事件處理
    TouchInputMapper::process(rawEvent);
    // 根據multi-touch protocol,解析原始事件存放到slot中
    mMultiTouchMotionAccumulator.process(rawEvent);
}

void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
    // 絕對坐標事件
    if (rawEvent->type == EV_ABS) {
        bool newSlot = false;
        if (mUsingSlotsProtocol) {
            // 獲取SLOT位置,一次Touch,Down/Move/.../Up通常使用一個SLOT存儲
            if (rawEvent->code == ABS_MT_SLOT) {
                mCurrentSlot = rawEvent->value;
                newSlot = true;
            }
        } else if (mCurrentSlot < 0) {
            mCurrentSlot = 0;
        }
        ......
        } else {
            Slot* slot = &mSlots[mCurrentSlot];

            switch (rawEvent->code) {
            // 一般只有Down/Move有坐標信息
            // X坐標
            case ABS_MT_POSITION_X:
                slot->mInUse = true;
                slot->mAbsMTPositionX = rawEvent->value;
                break;
            // Y坐標
            case ABS_MT_POSITION_Y:
                slot->mInUse = true;
                slot->mAbsMTPositionY = rawEvent->value;
                break;
            case ABS_MT_TOUCH_MAJOR:
                slot->mInUse = true;
                slot->mAbsMTTouchMajor = rawEvent->value;
                break;
            case ABS_MT_TOUCH_MINOR:
                slot->mInUse = true;
                slot->mAbsMTTouchMinor = rawEvent->value;
                slot->mHaveAbsMTTouchMinor = true;
                break;
            ......
            case ABS_MT_TRACKING_ID:
                // 通常Down/Move事件 id為非負,Up事件id = -1
                if (mUsingSlotsProtocol && rawEvent->value < 0) {
                    // The slot is no longer in use but it retains its previous contents,
                    // which may be reused for subsequent touches.
                    // 注意對于Up事件,mInUse為false
                    slot->mInUse = false;
                } else {
                    slot->mInUse = true;
                    slot->mAbsMTTrackingId = rawEvent->value;
                }
                break;
            }
        }
    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
        // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
        mCurrentSlot += 1;
    }
}

下面看TouchInputMapperprocess()實現

void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        // 獲得一個完整事件,處理事件后分發給監聽者
        sync(rawEvent->when);
    }
}

void TouchInputMapper::sync(nsecs_t when) {
    const RawState* last = mRawStatesPending.isEmpty() ?
            &mCurrentRawState : &mRawStatesPending.top();

    // Push a new state.
    // 創建一個RawState
    mRawStatesPending.push();
    RawState* next = &mRawStatesPending.editTop();
    next->clear();
    next->when = when;
    ......
    // Sync touch
    // 調用MultiTouchInputMapper的syncTouch,將SLOT中的原始事件保存到next中
    syncTouch(when, next);

    // Assign pointer ids.
    // 對于Up事件,由于pointer數為0,不分配id
    if (!mHavePointerIds) {
        assignPointerIds(last, next);
    }
    ......
    // 處理分發原始事件
    processRawTouches(false /*timeout*/);
}

下面首先看syncTouch()的實現,然后分析處理分發原始事件。

void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
    size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
    size_t outCount = 0;
    BitSet32 newPointerIdBits;

    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
        const MultiTouchMotionAccumulator::Slot* inSlot =
                mMultiTouchMotionAccumulator.getSlot(inIndex);
        // Up事件跳過
        if (!inSlot->isInUse()) {
            continue;
        }
        ......
        // SLOT中的原始事件信息保存到outState中
        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
        outPointer.x = inSlot->getX();
        outPointer.y = inSlot->getY();
        outPointer.pressure = inSlot->getPressure();
        outPointer.touchMajor = inSlot->getTouchMajor();
        outPointer.touchMinor = inSlot->getTouchMinor();
        outPointer.toolMajor = inSlot->getToolMajor();
        outPointer.toolMinor = inSlot->getToolMinor();
        outPointer.orientation = inSlot->getOrientation();
        outPointer.distance = inSlot->getDistance();
        outPointer.tiltX = 0;
        outPointer.tiltY = 0;
        ......
        // Assign pointer id using tracking id if available.
        mHavePointerIds = true;
        int32_t trackingId = inSlot->getTrackingId();
        int32_t id = -1;
        if (trackingId >= 0) {
            // 查找當前事件的trackingId在mPointerTrackingIdMap中的索引
            for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
                uint32_t n = idBits.clearFirstMarkedBit();
                // mPointerTrackingIdMap用于根據id快速訪問trackingId
                if (mPointerTrackingIdMap[n] == trackingId) {
                    id = n;
                }
            }

            if (id < 0 && !mPointerIdBits.isFull()) {
                id = mPointerIdBits.markFirstUnmarkedBit();
                mPointerTrackingIdMap[id] = trackingId;
            }
        }
        if (id < 0) {
            mHavePointerIds = false;
            outState->rawPointerData.clearIdBits();
            newPointerIdBits.clear();
        } else {
            // 記錄id(single pointer時通常為0)
            outPointer.id = id;
            outState->rawPointerData.idToIndex[id] = outCount;
            // 標記id掩碼
            outState->rawPointerData.markIdBit(id, isHovering);
            newPointerIdBits.markBit(id);
        }

        outCount += 1;
    }
    // pointer數這里為1
    outState->rawPointerData.pointerCount = outCount;
    // 更新全局id掩碼
    mPointerIdBits = newPointerIdBits;

    mMultiTouchMotionAccumulator.finishSync();
}
  • 對于Down/Move事件,SLOT中的原始事件信息保存到outState中,但是對于Up事件,outState處于初始狀態

下面分析processRawTouches()

void TouchInputMapper::processRawTouches(bool timeout) {
    if (mDeviceMode == DEVICE_MODE_DISABLED) {
        // Drop all input if the device is disabled.
        // 設備被禁止,丟棄輸入事件
        mCurrentRawState.clear();
        mRawStatesPending.clear();
        return;
    }
    ......
    const size_t N = mRawStatesPending.size();
    size_t count;
    for(count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];

        // A failure to assign the stylus id means that we're waiting on stylus data
        // and so should defer the rest of the pipeline.
        if (assignExternalStylusId(next, timeout)) {
            break;
        }

        // All ready to go.
        clearStylusDataPendingFlags();
        // 原始事件信息拷貝至mCurrentRawState
        mCurrentRawState.copyFrom(next);
        if (mCurrentRawState.when < mLastRawState.when) {
            mCurrentRawState.when = mLastRawState.when;
        }
        // 校準分發事件
        cookAndDispatch(mCurrentRawState.when);
    }
    if (count != 0) {
        // 移除處理完成的事件
        mRawStatesPending.removeItemsAt(0, count);
    }
    ......
}

下面看cookAndDispatch()的實現

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    // Always start with a clean state.
    mCurrentCookedState.clear();
    ......
    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure
    // with cooked pointer data that has the same ids and indices as the raw data.
    // The following code can use either the raw or cooked data, as needed.
    // 校準原始事件數據存放到mCurrentCookedState, Up事件,pointer數為0無需校準
    cookPointerData();
    ......
    if (mDeviceMode == DEVICE_MODE_POINTER) {
        ......
    } else {
        ......
        if (!mCurrentMotionAborted) {
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            // 分發Touch事件
            dispatchTouches(when, policyFlags);
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }
    }
    ......
    // Copy current touch to last touch in preparation for the next cycle.
    // 當前事件處理完成,拷貝至mLastRawState及mLastCookedState
    mLastRawState.copyFrom(mCurrentRawState);
    mLastCookedState.copyFrom(mCurrentCookedState);
}

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
    // 當前事件的id掩碼
    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
    // 先前的事件的id掩碼
    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
    int32_t metaState = getContext()->getGlobalMetaState();
    int32_t buttonState = mCurrentCookedState.buttonState;

    if (currentIdBits == lastIdBits) {
        if (!currentIdBits.isEmpty()) {
            // id掩碼相等且非零,那么current為Move事件
            // No pointer id changes so this is a move event.
            // The listener takes care of batching moves so we don't have to deal with that here.
            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
                    AMOTION_EVENT_EDGE_FLAG_NONE,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    currentIdBits, -1,
                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }
    } else {
        // There may be pointers going up and pointers going down and pointers moving
        // all at the same time.
        // 考慮single pointer事件
        // 1) Up事件(lastIdBits.value非0,currentIdBits.value為0)
        // 2) Down事件(lastIdBits.value為0,currentIdBits.value非0)
        BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
        BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
        BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
        BitSet32 dispatchedIdBits(lastIdBits.value);
        ......
        // Dispatch pointer up events.
        // 分發Up事件,這里注意Up事件信息使用mLastCookedState(例如,Up事件的坐標是前驅事件的坐標)
        while (!upIdBits.isEmpty()) {
            uint32_t upId = upIdBits.clearFirstMarkedBit();

            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
                    mLastCookedState.cookedPointerData.pointerProperties,
                    mLastCookedState.cookedPointerData.pointerCoords,
                    mLastCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
            dispatchedIdBits.clearBit(upId);
        }
        ......
        // Dispatch pointer down events using the new pointer locations.
        // 分發Down事件
        while (!downIdBits.isEmpty()) {
            uint32_t downId = downIdBits.clearFirstMarkedBit();
            dispatchedIdBits.markBit(downId);

            if (dispatchedIdBits.count() == 1) {
                // First pointer is going down.  Set down time.
                mDownTime = when;
            }

            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }
    }
}

下面看dispatchMotion()的實現

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
        int32_t action, int32_t actionButton, int32_t flags,
        int32_t metaState, int32_t buttonState, int32_t edgeFlags,
        const PointerProperties* properties, const PointerCoords* coords,
        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
        float xPrecision, float yPrecision, nsecs_t downTime) {
    PointerCoords pointerCoords[MAX_POINTERS];
    PointerProperties pointerProperties[MAX_POINTERS];
    uint32_t pointerCount = 0;
    // 拷貝指尖屬性及坐標數據
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        uint32_t index = idToIndex[id];
        pointerProperties[pointerCount].copyFrom(properties[index]);
        pointerCoords[pointerCount].copyFrom(coords[index]);

        if (changedId >= 0 && id == uint32_t(changedId)) {
            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
        }

        pointerCount += 1;
    }
    ......
    if (changedId >= 0 && pointerCount == 1) {
        // Replace initial down and final up action.
        // We can compare the action without masking off the changed pointer index
        // because we know the index is 0.
        // 修改single pointer Down/Up事件的action
        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            action = AMOTION_EVENT_ACTION_DOWN;
        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
            action = AMOTION_EVENT_ACTION_UP;
        } else {
            // Can't happen.
            ALOG_ASSERT(false);
        }
    }
    // 向監聽者(QueuedInputListener)發送事件
    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}

// NotifyMotionArgs存放到mArgsQueue容器中
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}
  • PointerCoords 保存指尖坐標數據
  • PointerProperties 保存指尖屬性信息
  • NotifyMotionArgs 描述一個Motion事件,采用命令模式

這里QueuedInputListener只是將事件封裝成NotifyArgs存放到容器中。回到InputReaderloopOnce()中,驅動上報的事件都放到容器中后,QueuedInputListener調用flush()分發給InputDispatcher。

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // mInnerListener為InputDispatcher
        // 這里args為NotifyMotionArgs
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

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

下面分析InputDispatchernotifyMotion()

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    // 檢查MotionEvent是否合法
    if (!validateMotionEvent(args->action, args->actionButton,
                args->pointerCount, args->pointerProperties)) {
        return;
    }

    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;
    // mPolicy為NativeInputManager
    // 檢查系統是否需要攔截事件
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);

    bool needWake;
    { // acquire lock
        mLock.lock();
        ......
        // Just enqueue a new motion event.
        MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
        // MotionEntry放入InboundQueue中
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        // 喚醒InputDispatcher處理事件
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    // 事件放入InboundQueue tail
    mInboundQueue.enqueueAtTail(entry);
    // 更新systrace中iq信息
    traceInboundQueueLengthLocked();

    ......
}

最終,InputReader線程將事件放入InboundQueue中,如果有必要,喚醒InputDispatcher線程處理事件。

小結

InputDispatcher發送MotionEvent到應用的過程

下面從InputDispatcherdispatchOnce()開始分析

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        // 如果沒有待處理的命令,分發事件(此過程中可能產生命令)
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        ......
    }

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    // 如果被喚醒,從此返回
    mLooper->pollOnce(timeoutMillis);
}

下面看dispatchOnceInnerLocked()的實現

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    ......
    // If dispatching is frozen, do not process timeouts or try to deliver any new events.
    // 事件分發凍住直接返回
    if (mDispatchFrozen) {
#if DEBUG_FOCUS
        ALOGD("Dispatch frozen.  Waiting some more.");
#endif
        return;
    }

    // Optimize latency of app switches.
    // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
    // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
    // 檢查app switch key處理是否超時
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            ......
        } else {
            // Inbound queue has at least one entry.
            // 從InboundQueue中取出一個事件
            mPendingEvent = mInboundQueue.dequeueAtHead();
            // 更新systrace中iq信息
            traceInboundQueueLengthLocked();
        }
        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            // 通知PowerManagerService更新電源狀態
            pokeUserActivityLocked(mPendingEvent);
        }

        // Get ready to dispatch the event.
        // 重置等待輸入目標超時信息
        resetANRTimeoutsLocked();
    }
    ......
    switch (mPendingEvent->type) {
    ......
    case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            // app switch key事件處理超時,丟棄后續事件
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            // 丟棄已經超時失效的事件
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            // 丟棄將要發送給不響應的應用的事件
            dropReason = DROP_REASON_BLOCKED;
        }
        // 分發事件
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
    ......
    }

    if (done) {
        // 分發結束清理
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

下面分析dispatchMotionLocked()實現

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
    if (! entry->dispatchInProgress) {
        // 標記正在分發事件
        entry->dispatchInProgress = true;

        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
    }

    // Clean up if dropping the event.
    if (*dropReason != DROP_REASON_NOT_DROPPED) {
        // 由于某種原因需要丟棄事件
        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
        return true;
    }

    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;

    // Identify targets.
    Vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        // 查找InputTarget
        // findTouchedWindowTargetsLocked的實現與WindowManagerService關系密切,這里
        // 暫不詳細討論,另外它也處理應用不響應輸入ANR的情況
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        ......
    }
    ......
    // 分發事件給inputTargets
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
  • InputTarget 描述如何將事件分發到特定的窗口,它包含目標窗口的InputChannel以及控制標志等

下面看dispatchEventLocked()的實現

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif

    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true

    pokeUserActivityLocked(eventEntry);

    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            // 通過InputChannel找到Connection
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            // 分發事件
            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().string());
#endif
        }
    }
}

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.
    // 如果Connection的狀態異常,跳過事件
    if (connection->status != Connection::STATUS_NORMAL) {
#if DEBUG_DISPATCH_CYCLE
        ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
                connection->getInputChannelName(), connection->getStatusLabel());
#endif
        return;
    }
    ......
    // Not splitting.  Enqueue dispatch entries for the event as is.
    // 分發事件
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    bool wasEmpty = connection->outboundQueue.isEmpty();
    ......
    // 事件封裝成DispatchEntry,放入Connection的OutboundQueue中,更新systrace中oq信息
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
            InputTarget::FLAG_DISPATCH_AS_IS);
    ......
    // If the outbound queue was previously empty, start the dispatch cycle going.
    // 如果之前Connection的OutboundQueue為空,啟動分發事件
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

下面看startDispatchCycleLocked()的實現

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ startDispatchCycle",
            connection->getInputChannelName());
#endif

    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        ......
        case EventEntry::TYPE_MOTION: {
            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);

            PointerCoords scaledCoords[MAX_POINTERS];
            const PointerCoords* usingCoords = motionEntry->pointerCoords;
            ......
            // Publish the motion event.
            // 通過socket將事件分發給應用
            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source,
                    dispatchEntry->resolvedAction, motionEntry->actionButton,
                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
                    motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
        }

        default:
            ALOG_ASSERT(false);
            return;
        }
        ......
        // Re-enqueue the event on the wait queue.
        // 從Connection的OutboundQueue中刪除事件
        connection->outboundQueue.dequeue(dispatchEntry);
        // 更新systrace中oq信息
        traceOutboundQueueLengthLocked(connection);
        // 事件添加到WaitQueue中
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        // 更新systrace中wq信息
        traceWaitQueueLengthLocked(connection);
    }
}

至此,輸入事件從InputDispatcher已經分發到應用。

小結

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,156評論 6 529
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,866評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 174,880評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,398評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,202評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,743評論 1 320
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,822評論 3 438
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 41,962評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,476評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,444評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,579評論 1 365
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,129評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,840評論 3 344
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,231評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,487評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,177評論 3 388
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,568評論 2 370

推薦閱讀更多精彩內容