Android U Input系统:InputReader 处理触摸事件

1,472 阅读12分钟

目标

本文分析的案例:手指在触摸屏上按下,移动,抬起。

SLOT 协议

驱动使用 A/B 协议来上报触摸屏事件,B 协议,通常也称之为 slot 协议,先简单介绍下这个协议是如何上报数据的

首先通过 adb shell getevent -l 监测驱动上报的数据

当手指按下时,会有如下数据

//type       //code              //value

EV_ABS       ABS_MT_SLOT          00000000
EV_ABS       ABS_MT_TRACKING_ID   00000000            
EV_ABS       ABS_MT_POSITION_X    000002ea            
EV_ABS       ABS_MT_POSITION_Y    00000534

EV_SYN       SYN_REPORT           00000000 

解释下这些数据的含义

  1. ABS_MT_SLOT 代表这一组数据,由哪个 slot 上报。
  2. ABS_MT_TRACKING_ID 代表这一组数据,属于哪个手指。
  3. ABS_MT_POSITION_X 或 ABS_MT_POSITION_Y,代表手指触摸的 x, y 坐标。
  4. SYN_REPORT ,它不属于触摸数据,它用来提示 Input 系统同步刚才发送的一组数据。

当手指移动时,会有如下数据

EV_ABS       ABS_MT_POSITION_X    000002ec            
EV_ABS       ABS_MT_POSITION_Y    00000526    

EV_SYN       SYN_REPORT           00000000 

这里只有坐标数据,并没有 slot 和 tracking id 数据,而这些缺失的数据,默认沿用上一次的数据。

当手指从屏幕上抬起时,会有如下数据

EV_ABS       ABS_MT_TRACKING_ID   ffffffff     

EV_SYN       SYN_REPORT           00000000    

没有 slot 数据,同样沿用上一次的 slot 数据。tracking id 的值为 -1,代表手指抬起。

InputReader 处理触摸事件

void InputReader::loopOnce() {
    // ...

    std::list<NotifyArgs> notifyArgs;
    
    // ...

    // 1. 从 EventHub 获取到事件
    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);

    { // acquire lock
        // ...

        // 2. 处理事件
        if (!events.empty()) {
            notifyArgs += processEventsLocked(events.data(), events.size());
        }

        // ...
    } // release lock

    // ...

    // 3. 把事件保存到 mQueuedListener,并刷新队列,把所有事件发送给下一环
    notifyAll(std::move(notifyArgs));
    mQueuedListener.flush();
}


std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    std::list<NotifyArgs> out;
    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;
            }


            if (debugRawEvents()) {
                ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
            }

            // 批量处理同一个设备的事件
            out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {

        }
        count -= batchSize;
        rawEvent += batchSize;
    }
    return out;
}

std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
                                                                const RawEvent* rawEvents,
                                                                size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return {};
    }

    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return {};
    }

    // 交给 InputDevice 处理
    return device->process(rawEvents, count);
}

在 InputReader 中,InputDevice 代表一个输入设备。InputReader 把属于同一个设备的数据,批量交给 InputDevice 处理

std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
    std::list<NotifyArgs> out;
    // 遍历所有待处于 raw events
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (debugRawEvents()) {
            const auto [type, code, value] =
                    InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,
                                                         rawEvent->value);
            ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,
                  rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);
        }

        if (mDropUntilNextSync) {
            
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
           
        } else {
            // 把 raw event 逐个交给所有 InputMapper 处理,得到一些加工后的事件
            for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
                out += mapper.process(rawEvent);
            });
        }
        --count;
    }

    // 返回经过 InputMapper 加工后的事件
    return out;
}

InputDevice 逐个地把 raw event 交给所有的 InputMapper 处理,InputMapper 处理完了之后,会把 raw event 的数据包装到新的事件中,然后返回。

MultiInputMapper 处理触摸事件

对于支持多点触摸的设备,InputMapper 为 MultiTouchInputMapper,来看下它如何处理触摸事件

std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    // 父类 TouchInputMapper 主要处理同步事件
    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);

    // 累加器来触摸事件数据
    mMultiTouchMotionAccumulator.process(rawEvent);
    return out;
}

MultiTouchInputMapper 使用父类处理同步事件,使用自己的累加器处理真正的触摸事件数据。

累加器处理触摸事件

void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
    if (rawEvent->type == EV_ABS) {
        bool newSlot = false;
        if (mUsingSlotsProtocol) {
            // 如果有 slot 数据,就获取 slot 索引,否则沿用上一次的 slot 索引
            if (rawEvent->code == ABS_MT_SLOT) {
                mCurrentSlot = rawEvent->value;
                newSlot = true;
            }
        } else if (mCurrentSlot < 0) {
            
        }

        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
            
        } else {
            Slot& slot = mSlots[mCurrentSlot];

            if (!mUsingSlotsProtocol) {
                
            }

            switch (rawEvent->code) {
                case ABS_MT_POSITION_X:
                    slot.mAbsMtPositionX = rawEvent->value;
                    warnIfNotInUse(*rawEvent, slot);
                    break;
                case ABS_MT_POSITION_Y:
                    slot.mAbsMtPositionY = rawEvent->value;
                    warnIfNotInUse(*rawEvent, slot);
                    break;
                
                // ...
            }
        }
    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
        
    }
}

很简单,根据 slot 索引,把触摸事件的数据,填充到数组 mSlots 对应的元素中。

举个例子,当手指按下时会有如下数据

EV_ABS       ABS_MT_SLOT          00000000
EV_ABS       ABS_MT_TRACKING_ID   00000000            
EV_ABS       ABS_MT_POSITION_X    000002ea            
EV_ABS       ABS_MT_POSITION_Y    00000534

EV_SYN       SYN_REPORT           00000000 

就是把第2、3、4行的数据,保存到 mSlot[0] 。

TouchInputMapper 处理同步事件

MultiTouchInputMapper 利用父类 TouchInputMapper 来处理同步事件

std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
    
    // ...

    std::list<NotifyArgs> out;

    // 处理同步事件
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        out += sync(rawEvent->when, rawEvent->readTime);
    }
    return out;
}

std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
    std::list<NotifyArgs> out;
    if (mDeviceMode == DeviceMode::DISABLED) {
        // Only save the last pending state when the device is disabled.
        mRawStatesPending.clear();
    }


    // 1.添加一个空元素
    mRawStatesPending.emplace_back();
    // 获取这个空元素,并填充数据
    RawState& next = mRawStatesPending.back();
    next.clear();
    next.when = when;
    next.readTime = readTime;

    // ...

    // 2.同步触摸数据,保存到 next 中
    // 由子类实现
    syncTouch(when, &next);

    // The last RawState is the actually second to last, since we just added a new state
    const RawState& last =
            mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];
    // ...
    // 同步触摸数据的过程中,如果没有手指id,那么沿用上一次事件的手指 id
    if (!mHavePointerIds) {
        assignPointerIds(last, next);
    }

    // ...

    // 3.处理同步过来的数据
    out += processRawTouches(/*timeout=*/false);
    return out;
}

TouchInputMapper 处理同步事件

  1. mRawStatesPending 队尾添加一个空元素。
  2. 把子类获取的数据,同步到 mRawStatesPending 最后一个元素中。
  3. 处理 mRawStatesPending 中同步过来的数据。
同步数据

MultiTouchInputMapper 同步触摸数据

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

    // 遍历所有 Slot 
    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
        // 获取 Slot 
        // Slot 保存的是 raw event 的数据
        const MultiTouchMotionAccumulator::Slot& inSlot =
                mMultiTouchMotionAccumulator.getSlot(inIndex);
        
        // ...

        // RawState::rawPointerData 不仅保存触摸事件的所有元数据,还会额外解析其他数据保存
        RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
        outPointer.x = inSlot.getX();
        outPointer.y = inSlot.getY();

        // ...

        // Assign pointer id using tracking id if available.
        if (mHavePointerIds) {
            // 获取 tracking id,有些 raw event 是不带 tracking id,沿用上一次 raw event 的 tracking id
            int32_t trackingId = inSlot.getTrackingId();
            int32_t id = -1;

            if (trackingId >= 0) { // 触摸事件中,指定了 tracking id
                for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {

                }

                if (id < 0 && !mPointerIdBits.isFull()) {
                    // 创建一个手指id
                    id = mPointerIdBits.markFirstUnmarkedBit();
                    // id -> trakcing id
                    mPointerTrackingIdMap[id] = trackingId;
                }
            }

            if (id < 0) {
                // 即使触摸事件没有指定 tracking id,在后面的流程中,会把前一个事件的手指 id 赋给它
                mHavePointerIds = false;
                outState->rawPointerData.clearIdBits();
                newPointerIdBits.clear();
            } else { // 触摸事件的数据中,指定了 tracking id
                // id 是数组 mPointerTrackingIdMap 的索引
                outPointer.id = id;
                // 保存 id -> index 的关系
                // id 是数组 mPointerTrackingIdMap 的索引
                // index 是数组 RawState::rawPointerData.pointers 的索引
                outState->rawPointerData.idToIndex[id] = outCount;
                outState->rawPointerData.markIdBit(id, isHovering);
                newPointerIdBits.markBit(id);
            }
        }
        outCount += 1;
    }

    // 保存手指的数量
    outState->rawPointerData.pointerCount = outCount;

    // mPointerIdBits 保存所有手指的索引,这个索引是数组 mPointerTrackingIdMap 的索引
    mPointerIdBits = newPointerIdBits;

    // 对于使用 slot 协议,累加器的完成同步,没有做什么
    mMultiTouchMotionAccumulator.finishSync();
}

这些代码看起来很头晕,简单来说,做了三件事

  1. 把累加器中数组 mSlot 中的数据,同步到 RawState::rawPointerData,当然,还会额外解析一些数据,例如手指数量。
  2. MultiTouchInputMapper 为 tracking id 生成一个新的 id。在实际使用中,是用这个新 id 代表手指 id 的。
  3. MultiTouchInputMapper 使用 mPointerIdBits 保存所有手指的 id。、

来看下同步数据所使用的数据结构

struct RawPointerData {
    // 保存触摸数据,例如 x, y 坐标
    struct Pointer {
        // id 是 MultiTouchInputMapper 为手指重新生成的 id,并不是实际的 tracking id
        uint32_t id{0xFFFFFFFF};
        int32_t x{};
        int32_t y{};
        
        // ...
    };

    // 手指的数量
    uint32_t pointerCount{};

    // 手指的触摸数据
    std::array<Pointer, MAX_POINTERS> pointers{};

    // id -> index 映射关系
    // id 是 MultiTouchInputMapper 为手指重新生成的 id,并不是实际的 tracking id
    // index 是数组 pointers 的索引
    // 这样可以通过手指 id ,找到对应手指的触摸数据
    IdToIndexArray idToIndex{};

};
处理同步后的数据

数据现在都同步到了 TouchInputMapper::mRawStatesPending 中,现在来处理它

std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) {
    std::list<NotifyArgs> out;
    
    // ...

    const size_t N = mRawStatesPending.size();
    size_t count;

    // 逐个处理 mRawStatesPending 中的数据
    for (count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];

        // ...

        // mCurrentRawState 保存当前正在处理的事件的数据
        mCurrentRawState = next;

        // ...

        // 加工和分发
        out += cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
    }

    if (count != 0) {
        mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
    }

    // ...
    return out;
}


std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
    std::list<NotifyArgs> out;

    // mCurrentCookedState 保存加工后的事件的数据
    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.
    // 1. 加工触摸事件
    cookPointerData();

    // ...

    if (mDeviceMode == DeviceMode::POINTER) {
        
    } else {
        if (!mCurrentMotionAborted) {
            // ...

            // 2. 创建用于分发的事件
            out += dispatchTouches(when, readTime, policyFlags);

            // ...
        }

        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
            mCurrentMotionAborted = false;
        }
    }

    // ...

    // Copy current touch to last touch in preparation for the next cycle.
    // 3.保存上次处理过的事件,以及加工后的事件,用于在下次循环中使用
    mLastRawState = mCurrentRawState;
    mLastCookedState = mCurrentCookedState;
    return out;
}

处理同步后的数据,就是逐个处理 mRawStatesPending 中保存的数据

  1. 对事件进行加工,例如把触摸屏坐标转换为显示屏坐标。
  2. 创建用于分发的事件。这一步是把 raw event ,转换为 ACTION DOWN, ACTION MOVE ,ACTION UP 这样的事件。
  3. 用 mLastRawState 保存上一次处理过的 raw event 数据,用 mLastCookedState 保存上一次加工后的事件的数据。
加工触摸事件
void TouchInputMapper::cookPointerData() {
    // mCurrentRawState 是当前正在处理的”同步后的事件”
    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;

    // 加工后的事件,保存到 mCurrentCookedState
    mCurrentCookedState.cookedPointerData.clear();
    mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
    mCurrentCookedState.cookedPointerData.touchingIdBits =
            mCurrentRawState.rawPointerData.touchingIdBits;

    // ...

    // Walk through the the active pointers and map device coordinates onto
    // display coordinates and adjust for display orientation.
    for (uint32_t i = 0; i < currentPointerCount; i++) {
        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];

        // ...

        vec2 transformed = {in.x, in.y};
        mAffineTransform.applyTo(transformed.x /*byRef*/, transformed.y /*byRef*/);
        // 把触摸屏坐标,转换成显示屏坐标
        transformed = mRawToDisplay.transform(transformed);

        // Write output coords.
        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
        out.clear();
        out.setAxisValue(AMOTION_EVENT_AXIS_X, transformed.x);
        out.setAxisValue(AMOTION_EVENT_AXIS_Y, transformed.y);

        // ...

        uint32_t id = in.id;
        
        // ...

        // Write output properties.
        PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
        properties.clear();
        properties.id = id;
        properties.toolType = in.toolType;

        // Write id index and mark id as valid.
        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
        mCurrentCookedState.cookedPointerData.validIdBits.markBit(id);
    }
}

所谓的加工,除了把 raw event 的数据转移到 mCurrentCookedState 中,还额外解析并保存了一些数据,例如,把触摸屏的坐标转换成显示屏的坐标。

生成用于分发的事件
std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime,
                                                        uint32_t policyFlags) {
    std::list<NotifyArgs> out;
    // 当前 cook 事件中的手指 id 索引
    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
    // 前一个 cook 事件的手指 id 索引
    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
    int32_t metaState = getContext()->getGlobalMetaState();
    int32_t buttonState = mCurrentCookedState.buttonState;

    if (currentIdBits == lastIdBits) { // 前后两次事件的 id 索引相等,表示是 MOVE 事件
        if (!currentIdBits.isEmpty()) {
            // 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.
            // 保存 move 事件
            out.push_back(
                    dispatchMotion(when, readTime, 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,
                                   MotionClassification::NONE));
        }
    } else {// 前后两次事件的 id 索引不相等

        // There may be pointers going up and pointers going down and pointers moving
        // all at the same time.
        // 对比前后两次事件的手指 id 索引,可以推测出是否有 down,move,up 事件
        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.
        while (!upIdBits.isEmpty()) { // 有手指抬起
            uint32_t upId = upIdBits.clearFirstMarkedBit();
            bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
            if (isCanceled) {
                ALOGI("Canceling pointer %d for the palm event was detected.", upId);
            }

            // 保存 up 事件
            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
                                         AMOTION_EVENT_ACTION_POINTER_UP, 0,
                                         isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState,
                                         buttonState, 0,
                                         mLastCookedState.cookedPointerData.pointerProperties,
                                         mLastCookedState.cookedPointerData.pointerCoords,
                                         mLastCookedState.cookedPointerData.idToIndex,
                                         dispatchedIdBits, upId, mOrientedXPrecision,
                                         mOrientedYPrecision, mDownTime,
                                         MotionClassification::NONE));
            dispatchedIdBits.clearBit(upId);
            mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
        }

        // ...

        // Dispatch pointer down events using the new pointer locations.
        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;
            }

            // 保存 down 事件
            out.push_back(
                    dispatchMotion(when, readTime, 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, MotionClassification::NONE));
        }
    }
    return out;
}

生成用于分发的事件的原理,其实就是对比前后两次数据,创建 ACTION DOWN, MOVE, UP 事件。例如,前后两次事件的手指相等,那么这次事件肯定是 MOVE 事件。

创建用于分发的事件,这里很有考究,来细致讲下

NotifyMotionArgs TouchInputMapper::dispatchMotion(
        nsecs_t when, nsecs_t readTime, 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 PropertiesArray& properties, const CoordsArray& coords,
        const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
        float yPrecision, nsecs_t downTime, MotionClassification classification) {
    PointerCoords pointerCoords[MAX_POINTERS];
    PointerProperties pointerProperties[MAX_POINTERS];
    uint32_t pointerCount = 0;

    // 1,
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        uint32_t index = idToIndex[id];
        // 保存id
        pointerProperties[pointerCount].copyFrom(properties[index]);
        // 保存触摸事件的信息,例如x,y坐标
        pointerCoords[pointerCount].copyFrom(coords[index]);

        // action 的前8位,是 数组pointerCoords 和 数组pointerProperties 的 index
        if (changedId >= 0 && id == uint32_t(changedId)) {
            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
        }

        pointerCount += 1;
    }

    ALOG_ASSERT(pointerCount != 0);

    // 2.
    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.
        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            // AMOTION_EVENT_ACTION_POINTER_DOWN 转换为 AMOTION_EVENT_ACTION_DOWN
            action = AMOTION_EVENT_ACTION_DOWN; 
        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
            if ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) {
                action = AMOTION_EVENT_ACTION_CANCEL;
            } else {
                // AMOTION_EVENT_ACTION_POINTER_UP 转化为 AMOTION_EVENT_ACTION_UP
                action = AMOTION_EVENT_ACTION_UP;
            }
        } else {
            // Can't happen.
            ALOG_ASSERT(false);
        }
    }

    
    // ...

    // 3 创建 NotifyMotionArgs
    return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                            policyFlags, action, actionButton, flags, metaState, buttonState,
                            classification, edgeFlags, pointerCount, pointerProperties,
                            pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                            downTime, std::move(frames));
}

第一步,在 action 的前8位,混入了 index(即 pointerCount),这个 index 是 数组pointerProperties 和 数组pointerCoords 的索引。这样有什么好处呢? 首先可以通过 action 的前八位,解析出 index,然后可以通过 index ,从 pointerProperties 中获取手指 id,或者从 pointerCoords 获取 x,y 坐标。

默认情况下,MOVE 事件的 action 统一为 AMOTION_EVENT_ACTION_POINTER_DOWN,up 事件的 action 统一为 AMOTION_EVENT_ACTION_POINTER_UP。但是,如果只有一个手指,那么会把 AMOTION_EVENT_ACTION_POINTER_DOWN 转换为 AMOTION_EVENT_ACTION_DOWN,把 AMOTION_EVENT_ACTION_POINTER_UP 转换为 AMOTION_EVENT_ACTION_UP。这就是第2步所做的。

app 开发时,使用的多点触摸的原理,就在这儿,请细品。

第三步,创建用于分发的事件 NotifyMotionArgs。这就是 InputReader 处理触摸事件最终的结果。