概述

MultiTouchInputMapper是Android输入系统中处理多点触摸事件的核心组件,它不仅负责处理原始触摸数据,还实现了复杂的手势识别算法。本文深入分析MultiTouchInputMapper的实现机制,包括触摸状态管理、多指跟踪、手势检测算法等关键技术。
AOSP源码基于 Android 7.0

MultiTouchInputMapper整体架构

┌─────────────────────────────────────────────────────────────┐
│                 MultiTouchInputMapper                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              原始事件处理                            │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │       MultiTouchMotionAccumulator           │   │   │
│  │  │  ┌─────────────────────────────────────┐   │   │   │
│  │  │  │      Slot-based Protocol B          │   │   │   │
│  │  │  │   (ABS_MT_SLOT, ABS_MT_TRACKING_ID) │   │   │   │
│  │  │  └─────────────────────────────────────┘   │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              触摸状态管理                            │   │
│  │  ┌─────────────────┐  ┌─────────────────────────┐  │   │
│  │  │   RawState      │  │     CookedState         │  │   │
│  │  │ (原始触摸数据)   │  │   (处理后的触摸数据)     │  │   │
│  │  └─────────────────┘  └─────────────────────────┘  │   │
│  └─────────────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              手势识别算法                            │   │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐  │   │
│  │  │  TAP手势     │ │ DRAG手势     │ │ ZOOM手势     │  │   │
│  │  └─────────────┘ └─────────────┘ └─────────────┘  │   │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐  │   │
│  │  │ SWIPE手势    │ │ FREEFORM手势 │ │ HOVER手势    │  │   │
│  │  └─────────────┘ └─────────────┘ └─────────────┘  │   │
│  └─────────────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              事件分发                               │   │
│  │  ┌─────────────────────────────────────────────┐   │   │
│  │  │            NotifyMotionArgs                 │   │   │
│  │  │     发送给InputDispatcher                   │   │   │
│  │  └─────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

1. MultiTouchInputMapper基础架构

1.1 继承关系和初始化

文件路径: frameworks/native/services/inputflinger/InputReader.cpp

// MultiTouchInputMapper继承自TouchInputMapper
class MultiTouchInputMapper : public TouchInputMapper {
public:
    MultiTouchInputMapper(InputDevice* device);
    virtual ~MultiTouchInputMapper();

    virtual void reset(nsecs_t when);
    virtual void process(const RawEvent* rawEvent);

protected:
    virtual void syncTouch(nsecs_t when, RawState* outState);
    virtual void configureRawPointerAxes();
    virtual bool hasStylus() const;

private:
    MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
    BitSet32 mPointerIdBits;
};

MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device)
    : TouchInputMapper(device) {
}

void MultiTouchInputMapper::reset(nsecs_t when) {
    mMultiTouchMotionAccumulator.reset(getDevice());
    mPointerIdBits.clear();
    TouchInputMapper::reset(when);
}

1.2 设备类型检测和配置

// frameworks/native/services/inputflinger/InputReader.cpp
// 在createDeviceLocked中根据设备类型创建相应的InputMapper
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

    // 触摸设备检测和Mapper创建
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        // 多点触摸设备 - 创建MultiTouchInputMapper
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        // 单点触摸设备 - 创建SingleTouchInputMapper
        device->addMapper(new SingleTouchInputMapper(device));
    }

    return device;
}

1.3 轴配置和校准

void MultiTouchInputMapper::configureRawPointerAxes() {
    TouchInputMapper::configureRawPointerAxes();

    // 配置多点触摸相关的轴信息
    getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
    getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
    getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
    getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
    getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
    getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
    getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
    getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
    getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
    getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
    getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);

    // 检查设备是否支持Slot-based Protocol B
    if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
        mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
        size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
        if (slotCount > MAX_SLOTS) {
            ALOGW("MultiTouch Device %s reported %zu slots but the framework "
                  "only supports a maximum of %zu slots at this time.",
                  getDeviceName().string(), slotCount, MAX_SLOTS);
            slotCount = MAX_SLOTS;
        }
        mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
    } else {
        mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, false /*usingSlotsProtocol*/);
    }
}

2. 原始事件处理:MultiTouchMotionAccumulator

2.1 Slot-based Protocol B

MultiTouchInputMapper支持Linux多点触摸协议B,这是一种基于槽位的协议:

事件类型事件代码说明
EV_ABSABS_MT_SLOT当前槽位选择
EV_ABSABS_MT_TRACKING_ID触点跟踪ID(-1表示触点离开)
EV_ABSABS_MT_POSITION_X触点X坐标
EV_ABSABS_MT_POSITION_Y触点Y坐标
EV_ABSABS_MT_PRESSURE触点压力
EV_ABSABS_MT_TOUCH_MAJOR触摸区域长轴
EV_ABSABS_MT_TOUCH_MINOR触摸区域短轴
EV_ABSABS_MT_ORIENTATION触摸方向

2.2 事件累积器结构

class MultiTouchMotionAccumulator {
public:
    class Slot {
    public:
        inline bool isInUse() const { return mInUse; }
        inline int32_t getX() const { return mAbsMTPositionX; }
        inline int32_t getY() const { return mAbsMTPositionY; }
        inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
        inline int32_t getTouchMinor() const { return mAbsMTTouchMinor; }
        inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
        inline int32_t getToolMinor() const { return mAbsMTWidthMinor; }
        inline int32_t getOrientation() const { return mAbsMTOrientation; }
        inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
        inline int32_t getPressure() const { return mAbsMTPressure; }
        inline int32_t getDistance() const { return mAbsMTDistance; }
        inline int32_t getToolType() const;

    private:
        bool mInUse;
        bool mHaveAbsMTPositionX;
        bool mHaveAbsMTPositionY;
        bool mHaveAbsMTTouchMajor;
        bool mHaveAbsMTTouchMinor;
        bool mHaveAbsMTWidthMajor;
        bool mHaveAbsMTWidthMinor;
        bool mHaveAbsMTOrientation;
        bool mHaveAbsMTTrackingId;
        bool mHaveAbsMTPressure;
        bool mHaveAbsMTDistance;
        bool mHaveAbsMTToolType;

        int32_t mAbsMTPositionX;
        int32_t mAbsMTPositionY;
        int32_t mAbsMTTouchMajor;
        int32_t mAbsMTTouchMinor;
        int32_t mAbsMTWidthMajor;
        int32_t mAbsMTWidthMinor;
        int32_t mAbsMTOrientation;
        int32_t mAbsMTTrackingId;
        int32_t mAbsMTPressure;
        int32_t mAbsMTDistance;
        int32_t mAbsMTToolType;
    };

private:
    Slot* mSlots;
    size_t mSlotCount;
    int32_t mCurrentSlot;
    bool mUsingSlotsProtocol;
};

2.3 原始事件处理

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    
    // 将原始事件传递给累积器处理
    mMultiTouchMotionAccumulator.process(rawEvent);
}

void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
    if (rawEvent->type == EV_ABS) {
        bool newSlot = false;
        if (mUsingSlotsProtocol) {
            if (rawEvent->code == ABS_MT_SLOT) {
                mCurrentSlot = rawEvent->value;
                newSlot = true;
            }
        } else if (mCurrentSlot < 0) {
            mCurrentSlot = 0;
        }

        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
            return; // 无效槽位
        }

        Slot* slot = &mSlots[mCurrentSlot];

        switch (rawEvent->code) {
        case ABS_MT_POSITION_X:
            slot->mAbsMTPositionX = rawEvent->value;
            slot->mHaveAbsMTPositionX = true;
            break;
        case ABS_MT_POSITION_Y:
            slot->mAbsMTPositionY = rawEvent->value;
            slot->mHaveAbsMTPositionY = true;
            break;
        case ABS_MT_TRACKING_ID:
            if (mUsingSlotsProtocol && rawEvent->value < 0) {
                // 触点离开
                slot->clear();
            } else {
                slot->mAbsMTTrackingId = rawEvent->value;
                slot->mHaveAbsMTTrackingId = true;
                slot->mInUse = true;
            }
            break;
        case ABS_MT_PRESSURE:
            slot->mAbsMTPressure = rawEvent->value;
            slot->mHaveAbsMTPressure = true;
            break;
        // ... 其他轴的处理
        }
    }
}

3. 触摸状态管理

3.1 状态数据结构

TouchInputMapper使用两个主要的状态结构来管理触摸数据:

struct RawState {
    nsecs_t when;
    
    // 原始触摸数据
    RawPointerData rawPointerData;
    
    // 按钮状态
    int32_t buttonState;
    
    // 滚轮状态
    int32_t rawVScroll;
    int32_t rawHScroll;
};

struct CookedState {
    // 处理后的触摸数据
    CookedPointerData cookedPointerData;
    
    // 按钮状态
    int32_t buttonState;
    
    // 手指ID位集
    BitSet32 fingerIdBits;
    BitSet32 stylusIdBits;
    BitSet32 mouseIdBits;
};

3.2 触摸数据转换: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);
        if (!inSlot->isInUse()) {
            continue;
        }

        if (outCount >= MAX_POINTERS) {
            break; // 触点数量超过限制
        }

        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;
        outPointer.toolType = inSlot->getToolType();
        outPointer.isHovering = (inSlot->getDistance() != 0);

        // 分配指针ID
        uint32_t id = inSlot->getTrackingId();
        if (id < 0) {
            id = mPointerIdBits.markFirstUnmarkedBit();
        }
        outPointer.id = id;
        outState->rawPointerData.idToIndex[id] = outCount;
        outState->rawPointerData.markIdBit(id, outPointer.isHovering);
        newPointerIdBits.markBit(id);

        outCount += 1;
    }

    outState->rawPointerData.pointerCount = outCount;
    mPointerIdBits = newPointerIdBits;
}

3.3 坐标变换和校准

void TouchInputMapper::cookPointerData() {
    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;

    mCurrentCookedState.cookedPointerData.clear();
    mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
    mCurrentCookedState.cookedPointerData.hoveringIdBits = 
            mCurrentRawState.rawPointerData.hoveringIdBits;
    mCurrentCookedState.cookedPointerData.touchingIdBits = 
            mCurrentRawState.rawPointerData.touchingIdBits;

    // 复制并转换每个触点的数据
    for (uint32_t i = 0; i < currentPointerCount; i++) {
        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
        CookedPointerData::Pointer& out = mCurrentCookedState.cookedPointerData.pointers[i];

        out.id = in.id;
        out.x = in.x;
        out.y = in.y;

        // 应用仿射变换(旋转、缩放、平移)
        float xTransformed = in.x, yTransformed = in.y;
        mAffineTransform.applyTo(xTransformed, yTransformed);
        
        // 应用表面参数转换
        float left, top, right, bottom;
        switch (mSurfaceOrientation) {
        case DISPLAY_ORIENTATION_90:
            left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
            right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
            break;
        case DISPLAY_ORIENTATION_180:
            left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
            top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
            right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
            bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
            break;
        case DISPLAY_ORIENTATION_270:
            left = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            right = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            break;
        default:
            left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
            bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            break;
        }

        out.x = (left + right) * 0.5f;
        out.y = (top + bottom) * 0.5f;

        // 计算压力、尺寸等其他属性
        out.pressure = in.pressure;
        out.touchMajor = in.touchMajor;
        out.touchMinor = in.touchMinor;
        out.toolMajor = in.toolMajor;
        out.toolMinor = in.toolMinor;
        out.orientation = in.orientation;
        out.distance = in.distance;
        out.toolType = in.toolType;
        out.isHovering = in.isHovering;
    }
}

4. 手势识别算法

4.1 手势类型定义

struct PointerGesture {
    enum Mode {
        NEUTRAL,                // 无手势
        QUIET,                  // 静默模式
        HOVER,                  // 悬停
        TAP,                    // 点击
        TAP_DRAG,              // 点击拖拽
        BUTTON_CLICK_OR_DRAG,  // 按钮点击或拖拽
        SWIPE,                 // 滑动
        FREEFORM,              // 自由形式
        PRESS,                 // 按压
    };

    Mode currentGestureMode;
    Mode lastGestureMode;

    nsecs_t downTime;
    int32_t activeGestureId;
    uint32_t activeTouchId;
    BitSet32 currentGestureIdBits;
    
    // 手势坐标和属性
    PointerProperties currentGestureProperties[MAX_POINTER_ID + 1];
    PointerCoords currentGestureCoords[MAX_POINTER_ID + 1];
    uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];

    // 速度跟踪
    VelocityTracker velocityTracker;
    
    // TAP手势参数
    nsecs_t tapDownTime;
    nsecs_t tapUpTime;
    float tapX, tapY;
    
    // SWIPE手势参数
    nsecs_t swipeDownTime;
    float swipeStartX, swipeStartY;
    
    // ZOOM手势参数
    float zoomCenterX, zoomCenterY;
    float zoomMagnification;
};

4.2 手势检测主函数

void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
        bool isTimeout) {
    
    // 更新手势检测器状态
    mPointerGesture.reset();
    
    // 获取当前触摸点信息
    uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
    uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
    
    // 更新速度跟踪器
    bool down, up, moved, resampled;
    mPointerGesture.velocityTracker.clear();
    BitSet32 idBits = mCurrentCookedState.fingerIdBits;
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        const RawPointerData::Pointer& pointer = 
                mCurrentRawState.rawPointerData.pointerForId(id);
        mPointerGesture.velocityTracker.addMovement(when, BitSet32::valueForBit(id),
                pointer.x, pointer.y);
    }
    
    // 根据当前状态和触摸点数量进行手势检测
    if (currentFingerCount == 0) {
        // 无触摸点 - 检测TAP手势或返回NEUTRAL
        detectTapGesture(when, &mPointerGesture);
    } else if (currentFingerCount == 1) {
        // 单点触摸 - 检测HOVER、TAP_DRAG或BUTTON_CLICK_OR_DRAG
        detectSingleTouchGesture(when, &mPointerGesture);
    } else {
        // 多点触摸 - 检测SWIPE、FREEFORM或PRESS手势
        detectMultiTouchGesture(when, currentFingerCount, &mPointerGesture);
    }
    
    // 分发手势事件
    dispatchPointerGestureMotion(when, policyFlags, mSource,
            mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits,
            mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords);
}

4.3 TAP手势检测

void TouchInputMapper::detectTapGesture(nsecs_t when, PointerGesture* outGesture) {
    // 检查是否从HOVER或TAP_DRAG状态转换而来
    if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
         mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
        mLastCookedState.fingerIdBits.count() == 1) {
        
        // 检查时间间隔
        if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
            float x, y;
            mPointerController->getPosition(&x, &y);
            
            // 检查距离是否在TAP容差范围内
            if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                
                // 确认为TAP手势
                mPointerGesture.tapUpTime = when;
                outGesture->currentGestureMode = PointerGesture::TAP;
                
                // 设置手势坐标
                outGesture->currentGestureCoords[0].clear();
                outGesture->currentGestureCoords[0].setAxisValue(
                        AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
                outGesture->currentGestureCoords[0].setAxisValue(
                        AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
                outGesture->currentGestureCoords[0].setAxisValue(
                        AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
                
                return;
            }
        }
    }
    
    // 不满足TAP条件,返回NEUTRAL状态
    outGesture->currentGestureMode = PointerGesture::NEUTRAL;
}

4.4 多点触摸手势检测

void TouchInputMapper::detectMultiTouchGesture(nsecs_t when, uint32_t fingerCount,
        PointerGesture* outGesture) {
    
    // 计算触摸点的质心
    float centroidX = 0, centroidY = 0;
    for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) {
        uint32_t id = idBits.clearFirstMarkedBit();
        const RawPointerData::Pointer& pointer = 
                mCurrentRawState.rawPointerData.pointerForId(id);
        centroidX += pointer.x;
        centroidY += pointer.y;
    }
    centroidX /= fingerCount;
    centroidY /= fingerCount;
    
    // 计算质心移动距离
    float deltaX = centroidX - mPointerGesture.referenceCentroidX;
    float deltaY = centroidY - mPointerGesture.referenceCentroidY;
    float distance = hypotf(deltaX, deltaY);
    
    // 计算触摸点间的距离变化(用于检测缩放)
    float currentSpan = calculateSpan();
    float lastSpan = mPointerGesture.referenceSpan;
    float spanDelta = currentSpan - lastSpan;
    
    if (mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
        mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
        mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
        
        // 判断是否为SWIPE手势
        if (fingerCount == 2 && distance > mConfig.pointerGestureMultitouchMinDistance) {
            // 计算两个触点的速度方向
            float vx1, vy1, vx2, vy2;
            bool hasVelocity = true;
            BitSet32 idBits(mCurrentCookedState.fingerIdBits);
            uint32_t id1 = idBits.clearFirstMarkedBit();
            uint32_t id2 = idBits.clearFirstMarkedBit();
            
            if (!mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1) ||
                !mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2)) {
                hasVelocity = false;
            }
            
            if (hasVelocity) {
                // 计算速度向量的夹角余弦值
                float dot = vx1 * vx2 + vy1 * vy2;
                float mag1 = hypotf(vx1, vy1);
                float mag2 = hypotf(vx2, vy2);
                float cosine = dot / (mag1 * mag2);
                
                if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
                    // 两个触点方向一致,判定为SWIPE
                    outGesture->currentGestureMode = PointerGesture::SWIPE;
                } else {
                    // 方向不一致,判定为FREEFORM(可能包含缩放、旋转)
                    outGesture->currentGestureMode = PointerGesture::FREEFORM;
                }
            }
        } else {
            // 多于2个触点或移动距离较小,判定为FREEFORM
            outGesture->currentGestureMode = PointerGesture::FREEFORM;
        }
    } else {
        // 初始状态,判定为PRESS
        outGesture->currentGestureMode = PointerGesture::PRESS;
        mPointerGesture.referenceCentroidX = centroidX;
        mPointerGesture.referenceCentroidY = centroidY;
        mPointerGesture.referenceSpan = currentSpan;
    }
    
    // 设置手势坐标(以质心为基准)
    outGesture->currentGestureCoords[0].clear();
    outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, centroidX);
    outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, centroidY);
    outGesture->currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
    
    // 如果是FREEFORM手势,还需要设置缩放信息
    if (outGesture->currentGestureMode == PointerGesture::FREEFORM && lastSpan > 0) {
        float scale = currentSpan / lastSpan;
        outGesture->currentGestureCoords[0].setAxisValue(
                AMOTION_EVENT_AXIS_GENERIC_1, scale);
    }
}

4.5 速度计算和平滑

float TouchInputMapper::calculateSpan() {
    BitSet32 idBits(mCurrentCookedState.fingerIdBits);
    if (idBits.count() < 2) {
        return 0;
    }
    
    // 找到距离最远的两个触点
    float maxDistance = 0;
    while (!idBits.isEmpty()) {
        uint32_t id1 = idBits.clearFirstMarkedBit();
        const RawPointerData::Pointer& pointer1 = 
                mCurrentRawState.rawPointerData.pointerForId(id1);
        
        BitSet32 remainingBits(idBits);
        while (!remainingBits.isEmpty()) {
            uint32_t id2 = remainingBits.clearFirstMarkedBit();
            const RawPointerData::Pointer& pointer2 = 
                    mCurrentRawState.rawPointerData.pointerForId(id2);
            
            float distance = hypotf(pointer1.x - pointer2.x, pointer1.y - pointer2.y);
            if (distance > maxDistance) {
                maxDistance = distance;
            }
        }
    }
    
    return maxDistance;
}

void TouchInputMapper::updatePointerGestureVelocity() {
    BitSet32 idBits = mCurrentCookedState.fingerIdBits;
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        const RawPointerData::Pointer& pointer = 
                mCurrentRawState.rawPointerData.pointerForId(id);
        
        mPointerGesture.velocityTracker.addMovement(
                mCurrentRawState.when, BitSet32::valueForBit(id),
                pointer.x, pointer.y);
    }
}

5. 触摸事件分发

5.1 事件分发流程

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
    BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
    BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
    int32_t metaState = getContext()->getGlobalMetaState();
    int32_t buttonState = mCurrentCookedState.buttonState;

    if (currentIdBits == lastIdBits) {
        if (!currentIdBits.isEmpty()) {
            // 无指针ID变化,这是一个移动事件
            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 {
        // 有指针ID变化,需要处理按下和抬起事件
        BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
        BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
        BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
        BitSet32 dispatchedIdBits(lastIdBits.value);

        // 分发指针抬起事件
        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);
        }

        // 分发移动事件
        if (!moveIdBits.isEmpty()) {
            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
                    metaState, buttonState, 0,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }

        // 分发指针按下事件
        while (!downIdBits.isEmpty()) {
            uint32_t downId = downIdBits.clearFirstMarkedBit();
            dispatchedIdBits.markBit(downId);

            if (dispatchedIdBits.count() == 1) {
                // 第一个指针按下,设置按下时间
                mDownTime = when;
            }

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

5.2 MotionEvent构造

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;
    }

    ALOG_ASSERT(pointerCount != 0);

    if (changedId >= 0 && pointerCount == 1) {
        // 对于单指事件,使用简化的动作码
        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 {
            // 不应该发生
            ALOG_ASSERT(false);
        }
    }

    // 创建并发送NotifyMotionArgs
    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            getDisplayId(), pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}

6. 性能优化和调试

6.1 关键性能优化

优化技术实现方式性能收益
槽位协议优化Protocol B减少数据传输降低CPU使用率
批量事件处理多个原始事件合并处理减少函数调用开销
内存池化重用触摸状态对象减少内存分配
速度计算优化增量式速度跟踪提高响应速度

6.2 调试开关和日志

// 调试开关定义
#define DEBUG_POINTERS 0            // 指针调试
#define DEBUG_GESTURES 0            // 手势调试
#define DEBUG_VIRTUAL_KEYS 0        // 虚拟按键调试
#define DEBUG_RAW_EVENTS 0          // 原始事件调试

// 关键调试日志
#if DEBUG_GESTURES
    ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
          "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
#endif

#if DEBUG_POINTERS
    ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
          "ignoring the rest.", getDeviceName().string(), MAX_POINTERS);
#endif

6.3 性能监控指标

监控指标获取方式用途
触摸延迟事件时间戳对比性能分析
手势识别准确率用户反馈统计算法优化
CPU使用率系统监控资源使用分析
内存使用对象计数内存泄漏检测

7. 总结

7.1 MultiTouchInputMapper核心价值

  1. 多点触摸支持: 完整实现Linux多点触摸协议B
  2. 智能手势识别: 支持TAP、DRAG、SWIPE、ZOOM等多种手势
  3. 精确坐标转换: 处理屏幕旋转、缩放和校准
  4. 高性能处理: 优化的事件处理和状态管理
  5. 可扩展架构: 支持不同类型的触摸设备

7.2 技术特点

  • 协议支持: 兼容Protocol A和Protocol B
  • 状态管理: 精确的触摸状态跟踪和转换
  • 手势算法: 基于几何和速度的智能识别
  • 坐标系统: 完整的仿射变换和校准支持
  • 性能优化: 槽位优化、批量处理等技术

7.3 系统架构意义

MultiTouchInputMapper作为触摸输入的核心处理器:

  • 承接硬件: 处理来自触摸屏驱动的原始数据
  • 智能识别: 将复杂的多点触摸转换为有意义的手势
  • 标准接口: 为上层应用提供标准化的触摸事件
  • 性能保障: 确保触摸系统的低延迟和高精度

相关文件路径

核心实现文件

  • frameworks/native/services/inputflinger/InputReader.cpp - MultiTouchInputMapper实现
  • frameworks/native/services/inputflinger/InputReader.h - MultiTouchInputMapper头文件

相关组件

  • MultiTouchMotionAccumulator - 多点触摸事件累积器
  • TouchInputMapper - 触摸输入基类
  • PointerGesture - 手势识别结构
  • VelocityTracker - 速度跟踪器

配置文件

  • frameworks/native/data/etc/ - 触摸设备配置
  • system/usr/idc/ - 输入设备特性文件
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐