Android U Input系统:扫描输入设备

1,904 阅读23分钟

本文分析设备扫描的过程,最主要的就是如何为输入设备建立数据。

EventHub 扫描输入设备

// EventHub.cpp

void EventHub::scanDevicesLocked() {
    status_t result;
    std::error_code errorCode;

    // 扫描 /dev/input 目录下的设备文件
    if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
        result = scanDirLocked(DEVICE_INPUT_PATH);
        if (result < 0) {
            ALOGE("scan dir failed for %s", DEVICE_INPUT_PATH);
        }
    } else {
        // ...
    }

    // ...
}

status_t EventHub::scanDirLocked(const std::string& dirname) {
    // 遍历 /dev/input 的目录项
    for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
        openDeviceLocked(entry.path());
    }
    return 0;
}

void EventHub::openDeviceLocked(const std::string& devicePath) {
    for (const auto& [deviceId, device] : mDevices) {
        if (device->path == devicePath) {
            return; // device was already registered
        }
    }

    char buffer[80];

    ALOGV("Opening device: %s", devicePath.c_str());

    // 1. 打开设备文件
    int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
    if (fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
        return;
    }

    // 2. InputDeviceIdentifier 保存输入设备的信息
    InputDeviceIdentifier identifier;

    // 获取设备名字
    if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        // 保存设备名
        identifier.name = buffer;
    }

    // 如果输入设备在排除列表中,那么关闭设备文件,那么系统就不会收到此输入设备的事件
    // mExcludedDevices 来自于上层 InputManagerService 
    for (size_t i = 0; i < mExcludedDevices.size(); i++) {
        const std::string& item = mExcludedDevices[i];
        if (identifier.name == item) {
            ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
            close(fd);
            return;
        }
    }

    // ... 省略一大堆的填充 identifier 的代码

    // 递增地分配一个 device id
    int32_t deviceId = mNextDeviceId++;

    // 3. 创建 Device,代表一个输入设备
    // Device 保存了刚才填充的输入设备信息 identifier
    std::unique_ptr<Device> device =
            std::make_unique<Device>(fd, deviceId, devicePath, identifier,
                                     obtainAssociatedDeviceLocked(devicePath));

    // 下面开始向 Device 中填充数据

    // 3.1 加载输入设备的配置文件
    // Device::configurationFile 保存配置文件路径
    // Device::configuration 保存配置文件数据
    device->loadConfigurationLocked();

    // 3.2 获取输入设备能报告哪些类型的事件
    // 例如,触摸屏能上报 type 为 EV_ABS 的坐标事件,按键设备能上报 type 为 EV_KEY 的按键事件
    device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
    device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);

    // 3.3 判断输入设备类型,保存到 device->classes
    bool haveKeyboardKeys =
            device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
    bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
            device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
    bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
            device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
    if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
        // 键盘类型
        device->classes |= InputDeviceClass::KEYBOARD;
    }

    // ...省略其他类型的判断代码

    if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
        if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
            // 多点触摸类型
            device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
            if (device->propBitmask.test(INPUT_PROP_POINTER) &&
                !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
                device->classes |= InputDeviceClass::TOUCHPAD;
            }
        }
        // Is this an old style single-touch driver?
    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
               device->absBitmask.test(ABS_Y)) {
        // 单点触摸类型
        device->classes |= InputDeviceClass::TOUCH;
    } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
               !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
        // ...
    }

    // ....

    // 4. 注意,键盘类型的输入设备,还需要加载按键映射文件
    status_t keyMapStatus = NAME_NOT_FOUND;
    if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |
                            InputDeviceClass::SENSOR)) {
        keyMapStatus = device->loadKeyMapLocked();
    }


    // ...

    // 5. epoll 监听输入设备的输入事件
    if (registerDeviceForEpollLocked(*device) != OK) {
        return;
    }

    // 6. 对 KEYBOARD 和 SENSOR 两种类型的设备,向驱动发送一些配置要求
    // 例如,对于 KEYBOARD 输入设备,禁止驱动的 key repeat,由输入系统自己模拟
    device->configureFd();

    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
          "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
          deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
          device->classes.string().c_str(), device->configurationFile.c_str(),
          device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
          toString(mBuiltInKeyboardId == deviceId));

    // 6. mOpeningDevices 保存输入设备 Device
    addDeviceLocked(std::move(device));
}


// 对于 keyboard 类型的输入设备,禁止驱动的 key repeat 功能,由 Input 系统自己生成 key repeat 事件
void EventHub::Device::configureFd() {
    // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
    if (classes.test(InputDeviceClass::KEYBOARD)) {
        // Disable kernel key repeat since we handle it ourselves
        unsigned int repeatRate[] = {0, 0};
        if (ioctl(fd, EVIOCSREP, repeatRate)) {
            ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno));
        }
    }

    // 》。。
}

EventHub 扫描输入设备,创建了代表输入设备的 Device,然后向 Device 中填充数据,Device 最终保存到 EventHub::mOpeningDevices 中 。

可以通过 dumpsys input 查看向 Device 中填充的数据

Event Hub State:
    
    ...

    // 触摸屏
    5: XXX_ts
      // 一个输入设备可以多多种类型
      Classes: KEYBOARD | TOUCH | TOUCH_MT | BATTERY
      
      Path: /dev/input/event5
      Enabled: true
      Descriptor: 037b4b9dd8120bd57454eabdb9b1462c7738b2d3
      Location: 
      ControllerNumber: 0
      UniqueId: xxx_ts
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000, bluetoothAddress=<not set>
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm

      // 输入设备配置文件
      ConfigurationFile: 

      VideoDevice: <none>
      SysfsDevicePath: /sys/devices/virtual
      KeyState (pressed): <none>
      AbsState: ABS_MT_SLOT=0, ABS_MT_TOUCH_MAJOR=12, ABS_MT_TOUCH_MINOR=12, ABS_MT_POSITION_X=4512, ABS_MT_POSITION_Y=10223, ABS_MT_TRACKING_ID=-1

    // 电源键的输入设备
    9: pmic_pwrkey
      Classes: KEYBOARD
      Path: /dev/input/event1
      Enabled: true
      Descriptor: 39f0cc2fda6f8287fd99ffa0db53ae80f4837e18
      Location: pmic_pwrkey/input0
      ControllerNumber: 0
      UniqueId: 
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000, bluetoothAddress=<not set>

      // 这两个都是输入设备的按键映射文件
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm

      // 输入设备配置文件
      ConfigurationFile: 

      VideoDevice: <none>
      SysfsDevicePath: /sys/devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8550@0:pon_hlos@1300/c42d000.qcom,spmi:qcom,pmk8550@0:pon_hlos@1300:pwrkey
      KeyState (pressed): <none>      

EventHub 生成合成事件

std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
    std::scoped_lock _l(mLock);

    std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;

    std::vector<RawEvent> events;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            // ...
        }

        // Report any devices that had last been added/removed.
        for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
            // ...
        }

        // mNeedToScanDevices 初始化的值为 true
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            // 1. 扫描输入设备
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        
        // 扫描设备时,mOpeningDevices 保存了所有打开的输入设备
        while (!mOpeningDevices.empty()) {
            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
            mOpeningDevices.pop_back();
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            
            // 2. 为打开的输入设备,向 events 中填充一个类型为 DEVICE_ADDED 的事件
            events.push_back({
                    .when = now,
                    .deviceId = deviceId,
                    .type = DEVICE_ADDED,
            });

            for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
                 it++) {
                // ...
            }

            // mDevices 以 id 为 KEY,保存 Device
            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
            if (!inserted) {
                ALOGW("Device id %d exists, replaced.", device->id);
            }
            
            // 表示需要发送一个完成扫描事件
            mNeedToSendFinishedDeviceScan = true;
            
            if (events.size() == EVENT_BUFFER_SIZE) {
                break;
            }
        }

        // 3. 扫描完成,向 events 中填充一个类型为 FINISHED_DEVICE_SCAN 的事件
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            events.push_back({
                    .when = now,
                    .type = FINISHED_DEVICE_SCAN,
            });
            if (events.size() == EVENT_BUFFER_SIZE) {
                break;
            }
        }

        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            // ...
        }

        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
           // ...
        }

        if (deviceChanged) {
            continue;
        }

        // 4. events 中已经有事件了,跳出无限循环
        if (!events.empty() || awoken) {
            break;
        }


        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        mLock.lock(); // reacquire lock after poll

        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // ...
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }

    // 返回事件给 InputReader
    return events;
}

EventHub 扫描输入设备后,向 events 中填充了两种类型的事情

  1. 为每一个打开的输入设备,添加一个类型为 DEVICE_ADDED 的事件。
  2. 设备扫描完成后,还补充了一个 FINISHED_DEVICE_SCAN 的事件。

由于这些事件都不是来自于输入设备,而是输入系统自己生成的,因此这些事件被称之为合成事件(synthetic event),而输入设备产生的事件,例如,按键事件/触摸事件,称之为元事件(raw event)。

InputReader 处理设备扫描的合成事件

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    // Copy some state so that we can access it outside the lock later.
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;

    // 保存需要发送给下一环的事件
    std::list<NotifyArgs> notifyArgs;

    { // acquire lock
        std::scoped_lock _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        auto changes = mConfigurationChangesToRefresh;
        if (changes.any()) {
            // ...
        } else if (mNextTimeout != LLONG_MAX) {
            // ...
        }
    } // release lock

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

    { // acquire lock
        std::scoped_lock _l(mLock);
        mReaderIsAliveCondition.notify_all();

        // 2. 处理事件
        if (!events.empty()) {
            // 处理 EventHub 的事件时,也会生成新的事件 NotifyArgs,保存到 notifyArgs 中
            notifyArgs += processEventsLocked(events.data(), events.size());
        }

        if (mNextTimeout != LLONG_MAX) {
            // ...

        // 3. 处理设备改变
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            inputDevices = getInputDevicesLocked();
            // 3.1 notifyArgs 保存一个 NotifyInputDevicesChangedArgs 事件
            // NotifyInputDevicesChangedArgs 不会发送给 InputDispatcher 处理,好像只是在 InputProcessor 中处理了
            notifyArgs.emplace_back(
                    NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});
        }
    } // release lock

    // 3.2 输入设备改变了,把所有输入设备信息发送给上层
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

    for (const auto& args : notifyArgs) {
        const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
        if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
            // ...
        }
    }

    // 4. 把 notifyArgs 中的所有事件,保存到 mQueuedListener 的队列中
    notifyAll(std::move(notifyArgs));

    // 5. 刷新 mQueuedListener 队列,把所有事件发送给下一环
    mQueuedListener.flush();
}

虽然说,InputReader 处理从 EventHub 获取的事件(RawEvent)时,会生成新的事件(NotifyArgs)。但是,对于 InputReader 处理设备扫描而生成的合成事件,本文只关心 InputReader 是如何为输入设备建立数据,至于这期间生成的 NotifyArgs 事件是如何处理的,我只在代码中进行注释。

// InputReader.cpp

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) {
            // ...
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                
                // ...

                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    // 如下函数所展示
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;

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

// 处理 type 为 FINISHED_DEVICE_SCAN 的事件
void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
    // Reset global meta state because it depends on the list of all configured devices.
    updateGlobalMetaStateLocked();

    // Enqueue configuration changed.
    // 创建 NotifyConfigurationChangedArgs ,并保存到 mQueuedListener 的队列中
    // 经过 InputDispatcher,最终通知上层 WMS 更新配置
    mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
}

// 处理 type 为 DEVICE_ADDED 的事件
void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
    if (mDevices.find(eventHubId) != mDevices.end()) {
        ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
    // 1. InputReader 创建的输入设备的类型为 InputDevice
    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);

    // 2. 配置 InputDevice
    notifyAll(device->configure(when, mConfig, /*changes=*/{}));
    notifyAll(device->reset(when));

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
              "(ignored non-input device)",
              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
    } else {
        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s",
              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
              inputEventSourceToString(device->getSources()).c_str());
    }

    // 3. InputReader 使用 mDevices 保存输入设备数据
    // mDevices : eventhub  id -> InputDevice
    mDevices.emplace(eventHubId, device);


    // Add device to device to EventHub ids map.
    // 4. InputReader 还使用 mDeviceToEventHubIdsMap 保存输入设备
    // mDeviceToEventHubIdsMap : InputDevice -> vector<eventhub id>
    const auto mapIt = mDeviceToEventHubIdsMap.find(device);
    if (mapIt == mDeviceToEventHubIdsMap.end()) {
        std::vector<int32_t> ids = {eventHubId};
        mDeviceToEventHubIdsMap.emplace(device, ids);
    } else {
        // TODO: 一个 InputDevice 可以对应多个 eventhub id,这是怎么回事?
        mapIt->second.push_back(eventHubId);
    }

    // mGeneration + 1 
    bumpGenerationLocked();

    // ...
}

InputReader 处理设备扫描而生成的合成事件,为输入设备创建 InputDevice。然后对 InputDevice 进行配置,这个配置的过程,就是为了处理输入设备的事件而准备的。最后用两个数据结构 mDevices (EventHubId->InputDevice) 和 mDeviceToEventHubIdsMap (InputDevice -> vector<EventHubId>) 保存了 InputDevice。

创建 InputDevice

// InputReader.cpp

std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
        const InputDeviceIdentifier identifier2 =
                devicePair.second->getDeviceInfo().getIdentifier();
        return isSubDevice(identifier, identifier2);
    });

    std::shared_ptr<InputDevice> device;
    if (deviceIt != mDevices.end()) {
        device = deviceIt->second;
    } else {
        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
        // 1.创建 InputDevice
        // mContext 是 InputReader 的环境,可以从它获取 InputReader 的数据
        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                               identifier);
    }
    // 2. 在 InputDevice 中创建 InputMapper 集合
    device->addEventHubDevice(eventHubId, mConfig);
    return device;
}

InputReader 创建了自己的输入设备 InputDevice,并且在 InputDevice 中,还会根据输入设备类型,创建并保存各种 InputMapper, 如下

InputMapper 是为了对 raw event 进行加工。例如,把 motion event 的屏幕坐标,转换为逻辑屏坐标。

// InputDevice.cpp

void InputDevice::addEventHubDevice(int32_t eventHubId,
                                    const InputReaderConfiguration& readerConfig) {
    if (mDevices.find(eventHubId) != mDevices.end()) {
        return;
    }

    // 创建 InputDeviceContext,它代表 InputDevice 的环境,它会传递给 InputMapper
    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
    // 为 InputDevice 创建 InputMapper 集合
    // 一个输入可能有多种类型,因此需要创建多个 InputMapper
    std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);

    // insert the context into the devices set
    // mDevices 保存 InputDeviceContext 和 InputMapper 集合
    // mDevices: EventHub device id -> { InputDeviceContext , vector<InputMapper> }
    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});

    // Must change generation to flag this device as changed
    // InputReader::mGeneration + 1
    bumpGeneration();
}

// 创建 InputMapper 集合
std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers(
        InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig) {
    ftl::Flags<InputDeviceClass> classes = contextPtr.getDeviceClasses();
    
    // 保存所有创建的 InputMapper
    std::vector<std::unique_ptr<InputMapper>> mappers;

    // ...

    uint32_t keyboardSource = 0;
    // 手机和平板的键盘类型的输入设备,就是无字母类型的键盘
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes.test(InputDeviceClass::KEYBOARD)) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    
    // ...
    
    // 为键盘输入设备,创建 KeyboradMapper
    if (keyboardSource != 0) {
        // 对于手机平板来说 keyboardSource 为 AINPUT_SOURCE_KEYBOARD
        mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig,
                                                                 keyboardSource, keyboardType));
    }

    // ...
    
    if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
        // ...
    } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
        // 为多点触摸的设备创建 MultiTouchInputMapper
        mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
    } else if (classes.test(InputDeviceClass::TOUCH)) {
        // 为单点触摸的设备创建 SingleTouchInputMapper
        mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
    }

    // ...
    return mappers;
}

配置 InputDevice

// InputDevice.cpp

// changes 此时为空
std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
                                             const InputReaderConfiguration& readerConfig,
                                             ConfigurationChanges changes) {
    std::list<NotifyArgs> out;
    mSources = 0;
    mClasses = ftl::Flags<InputDeviceClass>(0);
    mControllerNumber = 0;

    for_each_subdevice([this](InputDeviceContext& context) {
        // 获取 input device 的类型
        // 由 EventHub 实现
        mClasses |= context.getDeviceClasses();
        int32_t controllerNumber = context.getDeviceControllerNumber();
        if (controllerNumber > 0) {
            if (mControllerNumber && mControllerNumber != controllerNumber) {
                ALOGW("InputDevice::configure(): composite device contains multiple unique "
                      "controller numbers");
            }
            mControllerNumber = controllerNumber;
        }
    });

    mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
    mHasMic = mClasses.test(InputDeviceClass::MIC);

    using Change = InputReaderConfiguration::Change;

    if (!changes.any() || !isIgnored()) {
        // Full configuration should happen the first time configure is called
        // and when the device type is changed. Changing a device type can
        // affect various other parameters so should result in a
        // reconfiguration.
        if (!changes.any() || changes.test(Change::DEVICE_TYPE)) {
            mConfiguration.clear();
            for_each_subdevice([this](InputDeviceContext& context) {
                // 加载输入设备的配置文件的数据
                std::optional<PropertyMap> configuration =
                        getEventHub()->getConfiguration(context.getEventHubId());
                if (configuration) {
                    // InputDevice::mConfiguration 保存输入输入设备配置文件中的数据
                    mConfiguration.addAll(&(*configuration));
                }
            });

            mAssociatedDeviceType =
                    getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
        }

        if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
            if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
                // 从上层 KeyboardLayoutManager 加载 overlay keyboard layout
                std::shared_ptr<KeyCharacterMap> keyboardLayout =
                        mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
                bool shouldBumpGeneration = false;
                for_each_subdevice(
                        [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
                            if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
                                shouldBumpGeneration = true;
                            }
                        });
                if (shouldBumpGeneration) {
                    bumpGeneration();
                }
            }
        }

        if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
            if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
                // 从上层,加载输入设备别名
                std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
                if (mAlias != alias) {
                    mAlias = alias;
                    bumpGeneration();
                }
            }
        }

        if (changes.test(Change::ENABLED_STATE)) {
            // ...
        }

        if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
            // In most situations, no port or name will be specified.
            mAssociatedDisplayPort = std::nullopt;
            mAssociatedDisplayUniqueId = std::nullopt;
            mAssociatedViewport = std::nullopt;

            // Find the display port that corresponds to the current input port.
            // 处理 display port 与 Input port 的绑定关系
            const std::string& inputPort = mIdentifier.location;
            if (!inputPort.empty()) {
                // 端口绑定关系来自于上层
                const std::unordered_map<std::string, uint8_t>& ports =
                        readerConfig.portAssociations;
                const auto& displayPort = ports.find(inputPort);
                if (displayPort != ports.end()) {
                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
                } else {
                    const std::unordered_map<std::string, std::string>& displayUniqueIds =
                            readerConfig.uniqueIdAssociations;
                    const auto& displayUniqueId = displayUniqueIds.find(inputPort);
                    if (displayUniqueId != displayUniqueIds.end()) {
                        mAssociatedDisplayUniqueId = displayUniqueId->second;
                    }
                }
            }

            // If the device was explicitly disabled by the user, it would be present in the
            // "disabledDevices" list. If it is associated with a specific display, and it was not
            // explicitly disabled, then enable/disable the device based on whether we can find the
            // corresponding viewport.
            bool enabled =
                    (readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end());
            if (mAssociatedDisplayPort) {
                mAssociatedViewport =
                        readerConfig.getDisplayViewportByPort(*mAssociatedDisplayPort);
                if (!mAssociatedViewport) {
                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
                          "but the corresponding viewport is not found.",
                          getName().c_str(), *mAssociatedDisplayPort);
                    enabled = false;
                }
            } else if (mAssociatedDisplayUniqueId != std::nullopt) {
                mAssociatedViewport =
                        readerConfig.getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
                if (!mAssociatedViewport) {
                    ALOGW("Input device %s should be associated with display %s but the "
                          "corresponding viewport cannot be found",
                          getName().c_str(), mAssociatedDisplayUniqueId->c_str());
                    enabled = false;
                }
            }

            if (changes.any()) {
                // ...
            }
        }

        // 配置所有的 InputMapper
        for_each_mapper([this, when, &readerConfig, changes, &out](InputMapper& mapper) {
            // 参数 changes 为空
            out += mapper.reconfigure(when, readerConfig, changes);
            // InputDevice::mSources,是所有 InputMapper::mSource 的集合
            mSources |= mapper.getSources();
        });

        // If a device is just plugged but it might be disabled, we need to update some info like
        // axis range of touch from each InputMapper first, then disable it.
        if (!changes.any()) {
            out += setEnabled(readerConfig.disabledDevices.find(mId) ==
                                      readerConfig.disabledDevices.end(),
                              when);
        }
    }
    return out;
}

配置 InputDevice,有的根据 EventHub 中的数据配置,有的是根据上层的数据进行配置的,但是最主要是配置 InputMapper。

这里展示下按键输入设备和触摸屏的 dumpsys input 数据

  Device 7: vivo_ts
    EventHub Devices: [ 5 ] 
    Generation: 81
    IsExternal: false
    IsWaking: false
    AssociatedDisplayPort: <none>
    AssociatedDisplayUniqueIdByPort: <none>
    AssociatedDisplayUniqueIdByDescriptor: <none>
    HasMic:     false
    Sources: KEYBOARD | TOUCHSCREEN
    KeyboardType: 1
    ControllerNum: 0

  Device 3: pmic_pwrkey
    EventHub Devices: [ 9 ] 
    Generation: 69
    IsExternal: false
    IsWaking: false
    AssociatedDisplayPort: <none>
    AssociatedDisplayUniqueIdByPort: <none>
    AssociatedDisplayUniqueIdByDescriptor: <none>
    HasMic:     false
    Sources: KEYBOARD
    KeyboardType: 1
    ControllerNum: 0    

KeyboardInputMapper 的配置

// KeyboardInputMapper.cpp

// 此时 changes 为空
std::list<NotifyArgs> KeyboardInputMapper::reconfigure(nsecs_t when,
                                                       const InputReaderConfiguration& config,
                                                       ConfigurationChanges changes) {
    // 父类方法,实现为空
    std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);

    if (!changes.any()) { // first time only
        // Configure basic parameters.
        // 1.配置基本参数
        configureParameters();
    }

    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
        // 2.获取 DisplayViewPort
        // 键盘类型的输入设备,一般不需要 DisplayViewPort,这里不展开讨论
        mViewport = findViewport(config);
    }

    if (!changes.any() ||
        changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION)) {
        // 来自于上层
        std::optional<KeyboardLayoutInfo> newKeyboardLayoutInfo =
                getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
        if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
            mKeyboardLayoutInfo = newKeyboardLayoutInfo;
            bumpGeneration();
        }
    }

    return out;
}

KeyboardInputMapper 配置基本参数,是从输入设备的配置文件获取数据的,如下

// KeyboardInputMapper.cpp

void KeyboardInputMapper::configureParameters() {
    const PropertyMap& config = getDeviceContext().getConfiguration();
    // keyboard.orientationAware 如果为 true,那么按键映射需要根据方向进行转换
    mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
    // keyboard.handlesKeyRepeat 如果为 true,表示输入设备自己产生 key repeat 事件,而不需要 Input 系统模拟 key repeat 事件
    mParameters.handlesKeyRepeat = config.getBool("keyboard.handlesKeyRepeat").value_or(false);
    // TODO: 暂时还不知道有什么用
    mParameters.doNotWakeByDefault = config.getBool("keyboard.doNotWakeByDefault").value_or(false);
}

对于我的手机而言,输入设备是没有配置文件的,因此这些数据都为 false。

MultiTouchInputMapper 配置

手机或者平板的显示屏,基本上都是支持多点触摸,因此这里看来 MultiTouchInputMapper 的配置。

由于 MultiTouchInputMapper 没有实现 reconfigure(),因此看下它的父类 TouchInputMapper 的方法

// TouchInputMapper.cpp

// changes 此时为空
std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when,
                                                    const InputReaderConfiguration& config,
                                                    ConfigurationChanges changes) {
    // 父类实现为空
    std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);

    // 保存 InputReader 的配置
    mConfig = config;

    // Full configuration should happen the first time configure is called and
    // when the device type is changed. Changing a device type can affect
    // various other parameters so should result in a reconfiguration.
    if (!changes.any() || changes.test(InputReaderConfiguration::Change::DEVICE_TYPE)) {
        // Configure basic parameters.
        // 1. 配置基本参数
        mParameters = computeParameters(getDeviceContext());

        // Configure common accumulators.
        mCursorScrollAccumulator.configure(getDeviceContext());
        mTouchButtonAccumulator.configure();

        // Configure absolute axis information.
        // 2. 配置坐标系
        // 子类 MultiTouchInputMapper 有实现
        configureRawPointerAxes();

        // Prepare input device calibration.
        parseCalibration();
        resolveCalibration();
    }

    if (!changes.any() ||
        changes.test(InputReaderConfiguration::Change::TOUCH_AFFINE_TRANSFORMATION)) {
        // Update location calibration to reflect current settings
        updateAffineTransformation();
    }

    if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED)) {
        // Update pointer speed.
        mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
        mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
        mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
    }

    using namespace ftl::flag_operators;
    bool resetNeeded = false;
    if (!changes.any() ||
        changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
                    InputReaderConfiguration::Change::POINTER_CAPTURE |
                    InputReaderConfiguration::Change::POINTER_GESTURE_ENABLEMENT |
                    InputReaderConfiguration::Change::SHOW_TOUCHES |
                    InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE |
                    InputReaderConfiguration::Change::DEVICE_TYPE)) {
        // Configure device sources, display dimensions, orientation and
        // scaling factors.
        // 3. 配置输入设备的转换矩阵
        configureInputDevice(when, &resetNeeded);
    }

    if (changes.any() && resetNeeded) {
        out += reset(when);

        // Send reset, unless this is the first time the device has been configured,
        // in which case the reader will call reset itself after all mappers are ready.
        // InputDispatcher::dispatchDeviceResetLocked() 处理这个事件,就是重置一些数据而已
        out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
    }
    return out;
}

TouchInputMapper( 子类有 MultiTouchInputMapper 和 SingleTouchInputMapper ) 的配置过程,主要有以下几点

  1. 配置基本参数,保存到 TouchInputMapper::parameters。这些参数,大部分来自输入设备的配置文件,还有一些来自于驱动。参考【配置基本参数】。
  2. 配置输入设备的坐标系。例如,x,y 轴的最小和最大坐标。参考【配置输入设备坐标系】。
  3. 配置输入设备的转换矩阵。 这个是为了把输入设备的坐标,先转换为物理显示屏的坐标,然后再转换为逻辑显示屏的坐标。参考【配置输入设备的转换矩阵
配置基本参数
TouchInputMapper::Parameters TouchInputMapper::computeParameters(
        const InputDeviceContext& deviceContext) {
    Parameters parameters;
    // Use the pointer presentation mode for devices that do not support distinct
    // multitouch.  The spot-based presentation relies on being able to accurately
    // locate two or more fingers on the touch pad.
    // 这是从驱动中获取的
    parameters.gestureMode = deviceContext.hasInputProperty(INPUT_PROP_SEMI_MT)
            ? Parameters::GestureMode::SINGLE_TOUCH
            : Parameters::GestureMode::MULTI_TOUCH;

    // 加载输入设备配置文件的数据
    const PropertyMap& config = deviceContext.getConfiguration();
    
    
    std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
    if (gestureModeString.has_value()) {
        if (*gestureModeString == "single-touch") {
            parameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
        } else if (*gestureModeString == "multi-touch") {
            parameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
        } else if (*gestureModeString != "default") {
            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
        }
    }

    // 对于触摸屏,类型为 TOUCH_SCREEN
    parameters.deviceType = computeDeviceType(deviceContext);

    parameters.hasButtonUnderPad = deviceContext.hasInputProperty(INPUT_PROP_BUTTONPAD);

    // 注意,输入设备在没有配置文件的情况下,只要输入设备是触摸屏类型,parameters.orientationAware 就是为 true
    parameters.orientationAware =
            config.getBool("touch.orientationAware")
                    .value_or(parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);

    parameters.orientation = ui::ROTATION_0;
    std::optional<std::string> orientationString = config.getString("touch.orientation");
    if (orientationString.has_value()) {
        if (parameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
            ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
        } else if (*orientationString == "ORIENTATION_90") {
            parameters.orientation = ui::ROTATION_90;
        } else if (*orientationString == "ORIENTATION_180") {
            parameters.orientation = ui::ROTATION_180;
        } else if (*orientationString == "ORIENTATION_270") {
            parameters.orientation = ui::ROTATION_270;
        } else if (*orientationString != "ORIENTATION_0") {
            ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
        }
    }

    parameters.hasAssociatedDisplay = false;
    parameters.associatedDisplayIsExternal = false;
    if (parameters.orientationAware ||
        parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
        parameters.deviceType == Parameters::DeviceType::POINTER ||
        (parameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
         deviceContext.getAssociatedViewport())) {
        parameters.hasAssociatedDisplay = true;
        if (parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
            parameters.associatedDisplayIsExternal = deviceContext.isExternal();
            parameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
        }
    }
    if (deviceContext.getAssociatedDisplayPort()) {
        parameters.hasAssociatedDisplay = true;
    }

    // Initial downs on external touch devices should wake the device.
    // Normally we don't do this for internal touch screens to prevent them from waking
    // up in your pocket but you can enable it using the input device configuration.
    // touch.wake 如果为 true,那么触摸可以唤醒屏幕
    // 如果是外接设备,触摸也可以唤醒屏幕
    parameters.wake = config.getBool("touch.wake").value_or(deviceContext.isExternal());

    std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
    std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
    if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
        parameters.usiVersion = {
                .majorVersion = *usiVersionMajor,
                .minorVersion = *usiVersionMinor,
        };
    }

    parameters.enableForInactiveViewport =
            config.getBool("touch.enableForInactiveViewport").value_or(false);

    return parameters;
}

可以通过 dumpsys input 看下基本参数

  Device 7: xxx_ts
    ...
    Touch Input Mapper (mode - DIRECT):
      Parameters:
        GestureMode: MULTI_TOUCH
        DeviceType: TOUCH_SCREEN
        AssociatedDisplay: hasAssociatedDisplay=true, isExternal=false, displayId=''
        OrientationAware: true
        Orientation: Rotation0
        UsiVersion: <not set>
        EnableForInactiveViewport: false
配置输入设备坐标系
// MultiTouchInputMapper.cpp

void MultiTouchInputMapper::configureRawPointerAxes() {
    // 父类重置了 mRawPointerAxes
    TouchInputMapper::configureRawPointerAxes();

    // 获取坐标轴信息,保存到 mRawPointerAxes
    // getAbsoluteAxisInfo 由 EventHub 实现,是从驱动获取的数据
    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);

    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().c_str(), slotCount, MAX_SLOTS);
            slotCount = MAX_SLOTS;
        }
        // 配置累加器
        // 注意,最后一个参数,表示使用 slot 协议来上报数据
        mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
                                               /*usingSlotsProtocol=*/true);
    } else {
        // ...
    }
}

MultiTouchInputMapper 配置的坐标轴信息,都保存到 mRawPointerAxes,可以 dumpsys input 看下数据

Input Reader State (Nums of device: 9):
  ...
  Device 7: xxx_ts
    ...
    Touch Input Mapper (mode - DIRECT):
      Raw Touch Axes:
        X: min=0, max=12599, flat=0, fuzz=0, resolution=0
        Y: min=0, max=27999, flat=0, fuzz=0, resolution=0
        Pressure: unknown range
        TouchMajor: min=0, max=31, flat=0, fuzz=0, resolution=0
        TouchMinor: min=0, max=31, flat=0, fuzz=0, resolution=0
        ToolMajor: unknown range
        ToolMinor: unknown range
        Orientation: unknown range
        Distance: unknown range
        TiltX: unknown range
        TiltY: unknown range
        TrackingId: min=0, max=65535, flat=0, fuzz=0, resolution=0
        Slot: min=0, max=9, flat=0, fuzz=0, resolution=0    

MultiTouchInputMapper 还为触摸事件的累加器进行配置,如下

// MultiTouchMotionAccumulator.cpp

void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceContext,
                                            size_t slotCount, bool usingSlotsProtocol) {
    // usingSlotsProtocol 为 true
    mUsingSlotsProtocol = usingSlotsProtocol;
    // 保存 slot 的数量
    mSlots = std::vector<Slot>(slotCount);

    mCurrentSlot = -1;
    if (mUsingSlotsProtocol) {
        int32_t initialSlot;
        // EventHub 实现,从驱动中获取当前上报事件的 slot
        if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
            status == OK) {
            mCurrentSlot = initialSlot;
        } else {
            ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
        }
    }
}

配置 MultiTouchMotionAccumulator,主要就是保存是否是使用 slot 协议,以及 slot 的数量。

配置输入设备的转换矩阵
// TouchInputMapper.cpp

void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
    const DeviceMode oldDeviceMode = mDeviceMode;

    resolveExternalStylusPresence();

    // 1. 解析 device mode
    if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
        // ...
    } else if (isTouchScreen()) { // 触摸屏的 device type 是 TOUCH_SCREEN
        mSource = AINPUT_SOURCE_TOUCHSCREEN;
        // 触摸屏的 device mode 为 DIRECT
        mDeviceMode = DeviceMode::DIRECT;
        
        // ...
    } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
        // ...
    } else {
        // ...
    }

    // 2. 找到 DisplayViewport
    const std::optional<DisplayViewport> newViewportOpt = findViewport();

    // Ensure the device is valid and can be used.
    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
              "The device will be inoperable.",
              getDeviceName().c_str());
        mDeviceMode = DeviceMode::DISABLED;
    } else if (!newViewportOpt) {
        ALOGI("Touch device '%s' could not query the properties of its associated "
              "display.  The device will be inoperable until the display size "
              "becomes available.",
              getDeviceName().c_str());
        mDeviceMode = DeviceMode::DISABLED;
    } else if (!mParameters.enableForInactiveViewport && !newViewportOpt->isActive) {
        ALOGI("Disabling %s (device %i) because the associated viewport is not active",
              getDeviceName().c_str(), getDeviceId());
        mDeviceMode = DeviceMode::DISABLED;
    }

    // Raw width and height in the natural orientation.
    const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
    const int32_t rawXResolution = mRawPointerAxes.x.resolution;
    const int32_t rawYResolution = mRawPointerAxes.y.resolution;
    // Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
    const float rawMeanResolution =
            (rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;

    const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
    const bool viewportChanged = mViewport != newViewport;
    bool skipViewportUpdate = false;
    if (viewportChanged) {
        const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
        const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId;

        // 保存 DisplayViewPort
        mViewport = newViewport;

        // 触摸屏的 device mode 是 DIRECT
        if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
            const auto oldDisplayBounds = mDisplayBounds;

            mDisplayBounds = getNaturalDisplaySize(mViewport);

            mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
                                              mViewport.physicalRight, mViewport.physicalBottom};

            // TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that
            //     uses mInputDeviceOrientation. The new logic uses the transforms calculated in
            //     computeInputTransforms().
            // InputReader works in the un-rotated display coordinate space, so we don't need to do
            // anything if the device is already orientation-aware. If the device is not
            // orientation-aware, then we need to apply the inverse rotation of the display so that
            // when the display rotation is applied later as a part of the per-window transform, we
            // get the expected screen coordinates.
            mInputDeviceOrientation = mParameters.orientationAware
                    ? ui::ROTATION_0
                    : getInverseRotation(mViewport.orientation);
            // For orientation-aware devices that work in the un-rotated coordinate space, the
            // viewport update should be skipped if it is only a change in the orientation.
            skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
                    mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;

            // Apply the input device orientation for the device.
            // 计算输入设备的方向
            mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;

            // 3. 计算转换矩阵
            computeInputTransforms();
        } else {
            // ...
        }
    }

    // If moving between pointer modes, need to reset some state.
    bool deviceModeChanged = mDeviceMode != oldDeviceMode;
    if (deviceModeChanged) {
        mOrientedRanges.clear();
    }

    // Create and preserve the pointer controller in the following cases:
    const bool isPointerControllerNeeded =
            // - when the device is in pointer mode, to show the mouse cursor;
            (mDeviceMode == DeviceMode::POINTER) ||
            // - when pointer capture is enabled, to preserve the mouse cursor position;
            (mParameters.deviceType == Parameters::DeviceType::POINTER &&
             mConfig.pointerCaptureRequest.enable) ||
            // - when we should be showing touches;
            (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
            // - when we should be showing a pointer icon for direct styluses.
            (mDeviceMode == DeviceMode::DIRECT && mConfig.stylusPointerIconEnabled && hasStylus());
    if (isPointerControllerNeeded) {
        if (mPointerController == nullptr) {
            mPointerController = getContext()->getPointerController(getDeviceId());
        }
        if (mConfig.pointerCaptureRequest.enable) {
            mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
        }
    } else {
        if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
            !mConfig.showTouches) {
            mPointerController->clearSpots();
        }
        mPointerController.reset();
    }

    if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
        ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
              "display id %d",
              getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
              mInputDeviceOrientation, mDeviceMode, mViewport.displayId);

        configureVirtualKeys();

        initializeOrientedRanges();

        // Location
        updateAffineTransformation();

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

        // Inform the dispatcher about the changes.
        *outResetNeeded = true;
        bumpGeneration();
    }
}

既然是要通过转换矩阵,把输入设备的坐标,最终转化为逻辑显示屏的坐标,那么首先得找到显示屏的相关坐标数据,这就是第二步所做的,如下

// TouchInputMapper.cpp

std::optional<DisplayViewport> TouchInputMapper::findViewport() {

    // 对于触摸屏,通过导出的数据看,mParameters.hasAssociatedDisplay 为 true,mDeviceMode 为 DIRECT
    if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
        if (getDeviceContext().getAssociatedViewport()) {
            // ...
        }

        const std::optional<std::string> associatedDisplayUniqueId =
                getDeviceContext().getAssociatedDisplayUniqueId();
        if (associatedDisplayUniqueId) {
            // ...
        }

        // 触摸屏的 device mode 为 DIRECT
        if (mDeviceMode == DeviceMode::POINTER) {
            // ...
        }

        if (!mParameters.uniqueDisplayId.empty()) {
            // ...
        }

        ViewportType viewportTypeToUse;
        if (mParameters.associatedDisplayIsExternal) {
            // ...
        } else {
            // 默认使用内置显示屏的 DisplayViewPort
            viewportTypeToUse = ViewportType::INTERNAL;
        }

        std::optional<DisplayViewport> viewport =
                mConfig.getDisplayViewportByType(viewportTypeToUse);
        if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
            // ...
        }

        return viewport;
    }

    // ...
}

触摸屏,默认与内置显示屏的 DisplayViewPort 关联。而这个 DisplayViewPort 是 DisplayManagerService 设置给 Input 系统的。

那么,这个 DisplayViewPort 有什么用呢? 可以看下上层的注释

/**
 * Describes how the pixels of physical display device reflects the content of
 * a logical display.
 *
 * This information is used by the input system to translate touch input from
 * physical display coordinates into logical display coordinates.
 *.
 */
public final class DisplayViewport {

根据上层的 DisplayViewport 注释所说,它的作用是把物理显示屏的坐标,转换为逻辑显示屏的坐标。但是,在 Input 系统中,还会利用它把输入设备的坐标,转换为物理显示屏的坐标,最终转换为逻辑显示屏的坐标,如下

// TouchInputMapper.cpp
void TouchInputMapper::computeInputTransforms() {
    constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) {
        return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270;
    };

    // See notes about input coordinates in the inputflinger docs:
    // //frameworks/native/services/inputflinger/docs/input_coordinates.md

    // Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
    ui::Transform undoOffsetInRaw;
    undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);

    // Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates
    // will now be in the same orientation as the display in ROTATION_0.
    // Note: Negating an ui::Rotation value will give its inverse rotation.
    const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation);
    const ui::Size orientedRawSize = isRotated(inputDeviceOrientation)
            ? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()}
            : ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
    // When rotating raw values, account for the extra unit added when calculating the raw range.
    const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1,
                                           orientedRawSize.height - 1);

    // Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will
    // now be in the same orientation as the rotated display. There is no need to rotate the
    // coordinates to the display rotation if the device is not orientation-aware.
    const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation);
    const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation)
            ? ui::Size{orientedRawSize.height, orientedRawSize.width}
            : orientedRawSize;
    // When rotating raw values, account for the extra unit added when calculating the raw range.
    const auto rotateInRaw = mParameters.orientationAware
            ? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1)
            : ui::Transform();

    // Step 4: Scale the raw coordinates to the display space.
    // - In DIRECT mode, we assume that the raw surface of the touch device maps perfectly to
    //   the surface of the display panel. This is usually true for touchscreens.
    // - In POINTER mode, we cannot assume that the display and the touch device have the same
    //   aspect ratio, since it is likely to be untrue for devices like external drawing tablets.
    //   In this case, we used a fixed scale so that 1) we use the same scale across both the x and
    //   y axes to ensure the mapping does not stretch gestures, and 2) the entire region of the
    //   display can be reached by the touch device.
    // - From this point onward, we are no longer in the discrete space of the raw coordinates but
    //   are in the continuous space of the logical display.
    ui::Transform scaleRawToDisplay;
    const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width;
    const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height;
    if (mDeviceMode == DeviceMode::DIRECT) {
        // 矩阵中设置缩放,其实就是为了把输入设备坐标,转换为物理屏的坐标
        scaleRawToDisplay.set(xScale, 0, 0, yScale);
    } else if (mDeviceMode == DeviceMode::POINTER) {
        const float fixedScale = std::max(xScale, yScale);
        scaleRawToDisplay.set(fixedScale, 0, 0, fixedScale);
    } else {
        LOG_ALWAYS_FATAL("computeInputTransform can only be used for DIRECT and POINTER modes");
    }

    // Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space
    // that InputReader uses.
    const auto undoRotateInDisplay =
            ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight)
                    .inverse();

    // Now put it all together!

    // The transform that maps the input device's raw coordinate space to the rotated display's
    // coordinate space. This used to perform hit-testing of raw events with the physical frame in
    // the rotated coordinate space. See mPhysicalFrameInRotatedDisplay.
    mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw)));

    // The transform that maps the input device's raw coordinate space to the un-rotated display's
    // coordinate space. InputReader generates events in the un-rotated display's coordinate space.
    mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay);
    mRawRotation = ui::Transform{mRawToDisplay.getOrientation()};
}

矩阵的知识,我已经还给我的大学老师了,这里就不献丑去分析原理,就展示下 dumpsys input 导出的数据吧

  Device 7: vivo_ts
      
      ...

      Viewport INTERNAL: displayId=0, uniqueId=local:4630946650788219010, port=130, orientation=0, logicalFrame=[0, 0, 1080, 2400], physicalFrame=[0, 0, 1260, 2800], deviceSize=[1260, 2800], isActive=[1]
      DisplayBounds: 1260x2800
      PhysicalFrameInRotatedDisplay: Rect{0, 0, 1260, 2800}
      InputDeviceOrientation: Rotation0
      Translation and Scaling Factors:
        RawToDisplay Transform: (ROT_0) (SCALE )
            0.1000  0.0000  0.0000
            0.0000  0.1000  0.0000
            0.0000  0.0000  1.0000
        RawRotation Transform: (ROT_0) (IDENTITY)

总结

扫描输入设备,最终是为了给输入设备建立数据,我们最主要关心的是,这些数据来自哪里。至于这些数据有什么用途,等到我们碰到的时候,再来细细地体会。