亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Input系統(tǒng)之InputReader處理合成事件詳解

 更新時間:2022年11月11日 09:57:38   作者:大胃粥  
這篇文章主要為大家介紹了Input系統(tǒng)之InputReader處理合成事件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

Input系統(tǒng): InputReader 概要性分析 把 InputReader 的事件分為了兩類,一類是合成事件,例如設備的增、刪事件,另一類是元輸入事件,也就是操作設備產(chǎn)生的事件,例如手指在觸摸屏上滑動。

本文承接前文,以設備的掃描過程為例,分析合成事件的產(chǎn)生與處理過程。雖然設備的掃描過程只會生成部分合成事件,但是只要我們掌握了這個過程,其他的合成事件的生成以及處理也是水到渠成的事。

生成合成事件

EventHub 掃描設備以及生成合成事件的過程如下

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
    std::scoped_lock _l(mLock);
    struct input_event readBuffer[bufferSize];
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    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();) {
            // ...
        }
        // 1. 掃描輸入設備
        // mNeedToScanDevices 初始化的值為 true
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        // 2. 為掃描后打開的每一個輸入設備,填充一個類型為 DEVICE_ADDED 的事件
        // 掃描過程中會把設備保存到 mOpeningDevices 中。
        while (!mOpeningDevices.empty()) {
            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
            mOpeningDevices.pop_back();
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            // Try to find a matching video device by comparing device names
            for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
                 it++) {
                // ...
            }
            // 每次填充完事件,就把設備 Device 保存到 mDevices 中
            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
            if (!inserted) {
                ALOGW("Device id %d exists, replaced.", device->id);
            }
            // 表明你需要發(fā)送設備掃描完成事件
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
        // 3. 填充設備掃描完成事件
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
        // Grab the next input event.
        bool deviceChanged = false;
        // 處理 epoll 事件
        while (mPendingEventIndex < mPendingEventCount) {
            // ...
        }
        // 處理設備改變
        // mPendingEventIndex >= mPendingEventCount 表示處理完所有的輸入事件后,再處理設備的改變
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            // ...
        }
        // 設備發(fā)生改變,那么跳過當前循環(huán),在下一個循環(huán)的開頭處理設備改變
        if (deviceChanged) {
            continue;
        }
        // 4. 如果有事件,或者被喚醒,那么終止循環(huán),接下來 InputReader 會處理事件或者更新配置
        if (event != buffer || awoken) {
            break;
        }
        mPendingEventIndex = 0;
        mLock.unlock(); // release lock before poll
        // 此時沒有事件,并且也沒有被喚醒,那么超時等待 epoll 事件
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        mLock.lock(); // reacquire lock after poll
        if (pollResult == 0) {
            // 處理超時...
        }
        if (pollResult < 0) {
            // 處理錯誤...
        } else {
            // 保存待處理事件的數(shù)量
            mPendingEventCount = size_t(pollResult);
        }
    }
    // 5. 返回事件的數(shù)量
    return event - buffer;
}

EventHub 掃描設備以及生成合成事件的過程如下

  • 通過 scanDevicesLocked() 掃描輸入設備。掃描過程中會把設備保存到 mOpeningDevices 中。
  • 為每一個輸入設備,向 InputReader 提供的 buffer 中,填充一個類型為 DEVICE_ADDED 的事件。并且還要注意,EventHub 還會把輸入設備 Device 保存到 mDevices 中。
  • 向 InputReader 提供的 buffer 中,填充一個類型為 FINISHED_DEVICE_SCAN 的事件。
  • 現(xiàn)在 InputReader 提供的 buffer 中已經(jīng)有數(shù)據(jù)了,是時候返回給 InputReader 進行處理了。
  • 返回要處理事件的數(shù)量給 InputReader。

雖然,從這里我們已經(jīng)可以明確知道生成了什么類型的合成事件,但是我們的目的不止于此,因此我們深入看看 scanDevicesLocked() 是如何完成設備的掃描的

void EventHub::scanDevicesLocked() {
    // 掃描 /dev/input 目錄
    status_t result = scanDirLocked(DEVICE_PATH);
    // ...
}
status_t EventHub::scanDirLocked(const std::string& dirname) {
    // 遍歷打開目錄項
    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());
    // 打開設備文件
    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;
    }
    // 1. 從驅動獲取輸入設備廠商信息,并填充到 InputDeviceIdentifier
    InputDeviceIdentifier identifier;
    // Get device name.
    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;
    }
    // ...省略其他信息的獲取與填充過程...
    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    // 2. 創(chuàng)建代表輸入設備的 Device ,并填充數(shù)據(jù)
    std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);
    // 2.1 加載并解析輸入設備的配置文件,解析的結果保存到 EventHub::configuration 中
    device->loadConfigurationLocked();
    // ...
    // 2.2 查找輸入設備可以報告哪些種類的事件
    // EV_KEY 表示按鍵事件,鍵盤類型設備可以報告此類事件
    device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
    // EV_ABS 表示絕對坐標事件,觸摸類型設備可以報告此類事件
    device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
    // ...
    device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
    // 2.3 判斷輸入設備的類型,保存到 Device::classes 中
    if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
        // 支持多點觸摸的TP
        if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
            device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
        }
    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
               device->absBitmask.test(ABS_Y)) {
        // 只支持單點觸摸的TP
        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)) {
        // ...
    }
    // ... 省略其余輸入類型的判斷 ...
    // 3. epoll 管理打開的輸入設備描述符
    if (registerDeviceForEpollLocked(*device) != OK) {
        return;
    }
    // kernel 進行配置
    device->configureFd();
    // 4. 保存正在打開的輸入設備
    // 就是簡單的保存到 std::vector<std::unique_ptr<Device>> mOpeningDevices
    addDeviceLocked(std::move(device));
}

設備的掃描過程如下

從驅動中輸入設備廠商信息,并保存到 InputDeviceIdentifier 中。

創(chuàng)建代表輸入設備 Device 對象,并更新輸入設備的信息

  • 加載并解析輸入設備的配置文件(不包括鍵盤配置文件),請參考【加載并解析輸入設備的配置】。
  • 更新輸入設備能報告的事件類型,例如手機上觸摸屏能報告 EV_ABS 類型的事件,它是一個絕對坐標事件。
  • 根據(jù)設備能報告的事件類型,判斷輸入設備的類型。例如,設備能報告 EV_ABS 類型事件,那么它肯定是一個觸摸設備,類型肯定有 InputDeviceClass::TOUCH。
  • epoll 監(jiān)聽輸入設備描述符的事件,其實就是實時監(jiān)聽輸入設備的輸入事件的到來。
  • 保存正在打開輸入設備。其實就是把創(chuàng)建的 Device 對象保存到 mOpeningDevices 中。

EventHub 所創(chuàng)建的輸入設備的信息,可以通過 adb shell dumpsys input 查看

可以通過 dumpsys input 查看 EventHub 獲取的設備信息

Event Hub State:
  BuiltInKeyboardId: -2
  Devices:
    ...
    3: XXXTouchScreen
      Classes: KEYBOARD | TOUCH | TOUCH_MT
      Path: /dev/input/event2
      Enabled: true
      Descriptor: 4d66f665abaf83d5d35852472ba90bd54ccd79ae
      Location: input/ts
      ControllerNumber: 0
      UniqueId: 
      Identifier: bus=0x001c, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile: 
      VideoDevice: <none>
    ...
    7: gpio-keys
      Classes: KEYBOARD
      Path: /dev/input/event4
      Enabled: true
      Descriptor: 485d69228e24f5e46da1598745890b214130dbc4
      Location: gpio-keys/input0
      ControllerNumber: 0
      UniqueId: 
      Identifier: bus=0x0019, vendor=0x0001, product=0x0001, version=0x0100
      KeyLayoutFile: /system/usr/keylayout/gpio-keys.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile: 
      VideoDevice: <none>      

有幾點要說明下

  • 創(chuàng)建 Device 后,加載的配置文件,對應于這里的 ConfigurationFile。屬性 KeyLayoutFile 和 KeyCharacterMapFile 是代表鍵盤類輸入設備的按鍵布局文件和按鍵字符映射文件,它們是在按鍵事件的處理過程中加載的。
  • 輸入設備 XXXTouchScreen 的類型有三個 KEYBOARD,TOUCH,TOUCH_MT,從這可以看出,一個輸入設備可以報告多種類型的事件。

加載并解析輸入設備的配置

void EventHub::Device::loadConfigurationLocked() {
    // EventHub::configurationFile 保存配置文件的路徑
    // 注意,第二個參數(shù)為 InputDeviceConfigurationFileType::CONFIGURATION,表示加載以 idc 結尾的文件
    configurationFile =
            getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
                                 InputDeviceConfigurationFileType::CONFIGURATION);
    if (configurationFile.empty()) {
        ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
    } else {
        // 解析配置文件
        android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
                PropertyMap::load(configurationFile.c_str());
        if (!propertyMap.ok()) {
            ALOGE("Error loading input device configuration file for device '%s'.  "
                  "Using default configuration.",
                  identifier.name.c_str());
        } else {
            // EventHub::configuration 保存配置文件的數(shù)據(jù)
            configuration = std::move(*propertyMap);
        }
    }
}

加載并解析輸入設備的配置文件的過程如下

  • 獲取配置文件并保存到 EventHub::configurationFile 。
  • 解析并保存數(shù)據(jù)保存到 EventHub::configuration 。

下面分析的幾個函數(shù),是加載所有配置文件的基本函數(shù)

// frameworks/native/libs/input/InputDevice.cpp
// 注意,此時 type 為 InputDeviceConfigurationFileType::CONFIGURATION
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
        const InputDeviceIdentifier& deviceIdentifier,
        InputDeviceConfigurationFileType type) {
    if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
        if (deviceIdentifier.version != 0) {
            // Try vendor product version.
            std::string versionPath = getInputDeviceConfigurationFilePathByName(
                    StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
                            deviceIdentifier.vendor, deviceIdentifier.product,
                            deviceIdentifier.version),
                    type);
            if (!versionPath.empty()) {
                return versionPath;
            }
        }
        // Try vendor product.
        std::string productPath = getInputDeviceConfigurationFilePathByName(
                StringPrintf("Vendor_%04x_Product_%04x",
                        deviceIdentifier.vendor, deviceIdentifier.product),
                type);
        if (!productPath.empty()) {
            return productPath;
        }
    }
    // Try device name.
    return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
}
std::string getInputDeviceConfigurationFilePathByName(
        const std::string& name, InputDeviceConfigurationFileType type) {
    // Search system repository.
    std::string path;
    // Treblized input device config files will be located /product/usr, /system_ext/usr,
    // /odm/usr or /vendor/usr.
    const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
                                    getenv("ANDROID_ROOT")};
    for (size_t i = 0; i < size(rootsForPartition); i++) {
        if (rootsForPartition[i] == nullptr) {
            continue;
        }
        path = rootsForPartition[i];
        path += "/usr/";
        // 1. 依次從各個分區(qū)的的子目錄 /usr/ 下獲取配置文件
        appendInputDeviceConfigurationFileRelativePath(path, name, type);
        if (!access(path.c_str(), R_OK)) {
            return path;
        }
    }
    // Search user repository.
    // TODO Should only look here if not in safe mode.
    path = "";
    char *androidData = getenv("ANDROID_DATA");
    if (androidData != nullptr) {
        path += androidData;
    }
    path += "/system/devices/";
    // 2. 從 /data/system/devices/ 下獲取配置文件
    appendInputDeviceConfigurationFileRelativePath(path, name, type);
    if (!access(path.c_str(), R_OK)) {
        return path;
    }
    // Not found.
    return "";
}
static const char* CONFIGURATION_FILE_DIR[] = {
        "idc/",
        "keylayout/",
        "keychars/",
};
static const char* CONFIGURATION_FILE_EXTENSION[] = {
        ".idc",
        ".kl",
        ".kcm",
};
static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
        const std::string& name, InputDeviceConfigurationFileType type) {
    path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)];
    path += name;
    path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)];
}

所有的配置文件的路徑有很多,我用兩個正則表達式表示

/[product|system_ext|odm|vendor|system]/usr/[idc|keylayout|keychars]/name.[idc|kl|kcm]

/data/system/devices/[idc|keylayout|keychars]/name.[idc|kl|kcm]

idc 全稱是 input device configuration,用于定義輸入設備的基本配置。

kl 全稱是 key layout,用于定義按鍵布局。

kcm 全稱是 key character map,用于定義按鍵字符映射。

對于文件名 name,有如下幾種情況

  • Vendor_XXX_Product_XXX_Version_XXX(Version不為0的情況下)
  • Vendor_XXX_Product_XXX(Version為0的情況下)
  • 修正后的設備名(設備名中除了字母、數(shù)字、下劃線、破折號,其實字符都替換為下劃線),。
  • 對于鍵盤類型輸入設備,按鍵配置文件的名字還可以為 Generic。

可以通過 dumpsys input 查看輸入設備的所有配置文件

Event Hub State:
  BuiltInKeyboardId: -2
  Devices:
    ...
    3: XXXTouchScreen
      Classes: KEYBOARD | TOUCH | TOUCH_MT
      ...
      Identifier: bus=0x001c, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile: 
    ...
    7: gpio-keys
      Classes: KEYBOARD
      ...
      Identifier: bus=0x0019, vendor=0x0001, product=0x0001, version=0x0100
      KeyLayoutFile: /system/usr/keylayout/gpio-keys.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:     

我使用的觸摸屏是 XXXTouchScreen ,它的配置文件的文件名可以為 Vendor_0000_Product_0000.idcXXXTouchScreen.idc。

InputReader 處理合成事件

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        std::scoped_lock _l(mLock);
        oldGeneration = mGeneration;
        timeoutMillis = -1;
        // 處理配置更新
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            // 更新配置
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
    // 1. 從 EventHub 獲取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { // acquire lock
        std::scoped_lock _l(mLock);
        mReaderIsAliveCondition.notify_all();
        // 2. 處理事件
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }
        // 3. 處理輸入設備改變
        // 3.1 輸入設備改變,重新獲取輸入設備信息
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            inputDevices = getInputDevicesLocked();
        }
    } // release lock
    // 3.2 通知監(jiān)聽者,輸入設備改變了
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    // 4. 刷新隊列中緩存的事件,分發(fā)事件給 InputClassifier。
    mQueuedListener->flush();
}

InputReader 處理合成事件的過程如下

  • 使用 processEventsLocked() 處理合成事件。
  • 如果設備發(fā)生改變,更新輸入設備信息后,通知監(jiān)聽者。其實就是把輸入設備信息更新到上層的 InputManagerService。
  • 刷新隊列,分發(fā)事件給 InputClassifier。

本文只分析 processEventsLocked() 如何處理合成事件。

EventHub 的設備掃描,生成了兩種類型的事件,一種為 EventHubInterface::DEVICE_ADDED, 另一種為 EventHubInterface::FINISHED_DEVICE_SCAN。現(xiàn)在看下這兩種事件的處理過程

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            // ... 處理元輸入事件
        } 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;
    }
}

EventHubInterface::FINISHED_DEVICE_SCAN 類型事件的處理比較簡單,如下

void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
    // 匯總所有鍵盤類型輸入設備的meta鍵狀態(tài)
    // Reset global meta state because it depends on the list of all configured devices.
    updateGlobalMetaStateLocked();
    // 添加一個代表配置改變的事件到隊列中
    // Enqueue configuration changed.
    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
    mQueuedListener->notifyConfigurationChanged(&args);
}

EventHubInterface::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. 創(chuàng)建輸入設備 InputDevice
    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
    // 1.2 使用InputReader的配置mConfig,對輸入設備進行配置
    device->configure(when, &mConfig, 0);
    device->reset(when);
    // 2. 保存輸入設備
    mDevices.emplace(eventHubId, device);
    // InputDevice -> vector<EventHubId>.
    const auto mapIt = mDeviceToEventHubIdsMap.find(device);
    if (mapIt == mDeviceToEventHubIdsMap.end()) {
        std::vector<int32_t> ids = {eventHubId};
        mDeviceToEventHubIdsMap.emplace(device, ids);
    } else {
        mapIt->second.push_back(eventHubId);
    }
    // mGeneration 加 1,表示輸入設備改變了
    bumpGenerationLocked();
    // ...
}

InputReader 對 EventHubInterface::DEVICE_ADDED 類型的事件的處理過程如下

  • 創(chuàng)建代表輸入設備的 InputDevice,然后使用 InputReader 的配置對它進行配置。
  • 用數(shù)據(jù)結構保存 InputDevice,最主要是使用 mDevices 保存。

分析到這里,其實已經(jīng)明白了整個設備掃描的過程。最后,我們重點分析下 EventHub 如何創(chuàng)建輸入設備 InputDevice,以及如何配置它。詳見【創(chuàng)建與配置 InputDevice

創(chuàng)建與配置 InputDevice

std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
        return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
                devicePair.second->getDescriptor() == identifier.descriptor;
    });
    std::shared_ptr<InputDevice> device;
    if (deviceIt != mDevices.end()) {
        device = deviceIt->second;
    } else {
        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
        // 創(chuàng)建 InputDevice
        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                               identifier);
    }
    // InputDevice 創(chuàng)建 InputMapper 集合
    device->addEventHubDevice(eventHubId);
    return device;
}
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
    if (mDevices.find(eventHubId) != mDevices.end()) {
        return;
    }
    // 1. 根據(jù)輸入設備類型,創(chuàng)建 InputMapper 集合
    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
    Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();
    std::vector<std::unique_ptr<InputMapper>> mappers;
    // Touchscreens and touchpad devices.
    if (classes.test(InputDeviceClass::TOUCH_MT)) {
        mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
    } else if (classes.test(InputDeviceClass::TOUCH)) {
        mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
    }
    // 省略其實類型的輸入設備的 InputMapper 的創(chuàng)建與保存的過程 ...
    // 2. 保存 InputMapper 集合
    // insert the context into the devices set
    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
    // InputReader 的 mGerneration 加 1,表示設備改變 
    // Must change generation to flag this device as changed
    bumpGeneration();
}

InputReade 創(chuàng)建 InputDevice 的過程如下

  • 創(chuàng)建 InputDevice 實例。
  • 為 InputDevice 創(chuàng)建 InputMapper 集合。

由于一個輸入設備可能有多種類型,而每一種類型對應一個 InputMapper,因此 InputDevice 會擁有多個 InputMapper。當一個輸入設備上報某一種類型的事件時,InputDevice 會把這個事件交給所有的 InputMapper 處理,而 InputMapper 根據(jù)事件的類型就知道是不是要處理這個事件。

InputMapper 的作用是對輸入事件進行加工,例如,把觸摸屏的坐標轉換為顯示屏的坐標,然后把加工后的事件放入 QueuedInputListener 的隊列中。InputReader 把從 EventHub 獲取的所有事件都處理完畢后,會刷新 QueuedInputListener 的隊列,也就是把事件發(fā)送給 InputClassifier。

EventHub 中使用 Device 代表輸入設備,而 InputReader 使用 InputDevice 代表輸入設備,它們之間的差別就是這個 InputMapper。也就是說 InputDevice 能處理事件,而 Device 只能保存輸入設備信息。

現(xiàn)在看下如何對 InputDevice 進行配置

void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                            uint32_t changes) {
    mSources = 0;
    mClasses = Flags<InputDeviceClass>(0);
    mControllerNumber = 0;
    for_each_subdevice([this](InputDeviceContext& context) {
        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);
    // InputDevice 如果沒有 InputMapper ,那么會被忽略
    if (!isIgnored()) {
        if (!changes) { // first time only
            mConfiguration.clear();
            // 遍歷所有 InuptMapper 的 InputDeviceContext
            for_each_subdevice([this](InputDeviceContext& context) {
                PropertyMap configuration;
                // 從 EventHub 獲取配置文件
                context.getConfiguration(&configuration);
                // 保存輸入設備所有的配置文件
                mConfiguration.addAll(&configuration);
            });
        }
        if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
            if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
                // 從上層InputManagerService獲取按鍵布局的配置
                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 || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
            if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
                // 從上層InputManagerService獲取設備別名的配置
                std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
                if (mAlias != alias) {
                    mAlias = alias;
                    bumpGeneration();
                }
            }
        }
        if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
            // 從InputReader的配置mConfig中,檢測輸入設備是否被拆除在外,并相應設置設備的 enabled 狀態(tài)
            auto it = config->disabledDevices.find(mId);
            bool enabled = it == config->disabledDevices.end();
            setEnabled(enabled, when);
        }
        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
            // ...
        }
        // 重點: 使用InputReader的配置mConfig,對InputMapper進行配置
        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
            mapper.configure(when, config, changes);
            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) {
            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
        }
    }
}

對 InputDevice 進行配置的數(shù)據(jù)源有如下幾個

  • EventHub。
  • 上層的 InputManagerService。
  • InputReader 的配置 mConfig。

我們不用糾結配置 InputDevice 的數(shù)據(jù)是什么,因為可以通過 adb shell dumpsys input 導出 InputDevice 的配置

Input Reader State (Nums of device: 8):
Device 4: XXXTouchScreen
    EventHub Devices: [ 5 ] 
    Generation: 59
    IsExternal: false
    AssociatedDisplayPort: <none>
    AssociatedDisplayUniqueId: <none>
    HasMic:     false
    Sources: 0x00001103
    KeyboardType: 1
    ControllerNum: 0
    ...

在對 InputDevice 的配置過程中,有一個很重要的地方,那就是對 InputMapper 進行配置。

對于觸摸屏設備而言,它的所有的 InputMapper 都有一個公共的父類 TouchInputMapper,如果這個觸摸屏支持多點觸摸,這個 InputMapper 就是 MultiTouchInputMapper,而如果只支持單點觸摸,那么這個 InputMapper 就是 SingleTouchInputMapper。

我們以手機的觸摸屏為例,它的 InputMapper 的配置過程是在 TouchInputMapper 中完成的

// 注意,參數(shù) config 是 InputReader 的配置 mConfig
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                 uint32_t changes) {
    // 父類實現(xiàn)為空                               
    InputMapper::configure(when, config, changes);
    // TouchInputMapper 還保存了 InputReader 的配置
    mConfig = *config;
    if (!changes) { // first time only
        // Configure basic parameters.
        // 1. 配置基本的參數(shù),參數(shù)保存到 TouchInputMapper::mParameters
        configureParameters();
        // Configure common accumulators.
        mCursorScrollAccumulator.configure(getDeviceContext());
        mTouchButtonAccumulator.configure(getDeviceContext());
        // Configure absolute axis information.
        // 2. 配置坐標系
        // 由子類實現(xiàn)
        configureRawPointerAxes();
        // Prepare input device calibration.
        parseCalibration();
        resolveCalibration();
    }
    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
        // Update location calibration to reflect current settings
        updateAffineTransformation();
    }
    if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
        // Update pointer speed.
        mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
        mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
        mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
    }
    bool resetNeeded = false;
    if (!changes ||
        (changes &
         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
          InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
          InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
          InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
        // Configure device sources, surface dimensions, orientation and
        // scaling factors.
        // 3. 配置 surface
        configureSurface(when, &resetNeeded);
    }
    if (changes && resetNeeded) {
        // 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.
        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
        getListener()->notifyDeviceReset(&args);
    }
}

對于觸摸屏而言,配置 InputMapper 的主要過程如下

  • 通過 configureParameters() 配置基本的參數(shù)。什么是基本參數(shù)?按我的理解,就是保持不變的參數(shù)。這些基本參數(shù)會保存到 InputDevice::mParameters 中。詳見【配置基本參數(shù)
  • 通過 configureRawPointerAxes() 配置坐標系,不過這個函數(shù)由子類實現(xiàn),對于支持多點觸摸的輸入設備,子類就是 MultiTouchInputMapper。其實就是獲取觸摸屏的坐標系信息,例如 x, y 方向的坐標點的信息(例如,最大值,最小值)。詳見【配置坐標系
  • 通過 configureSurface() 配置 surface 的參數(shù)。這些參數(shù)決定了如何從觸摸屏坐標轉換為顯示屏坐標?!?strong>配置 Surface】

配置基本參數(shù)

void TouchInputMapper::configureParameters() {
    mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
            ? Parameters::GestureMode::SINGLE_TOUCH
            : Parameters::GestureMode::MULTI_TOUCH;
    String8 gestureModeString;
    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
                                                             gestureModeString)) {
        if (gestureModeString == "single-touch") {
            mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
        } else if (gestureModeString == "multi-touch") {
            mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
        } else if (gestureModeString != "default") {
            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
        }
    }
    // 通過 EventHub 從驅動獲取信息,決定設備類型 mParameters.deviceType
    if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
        // The device is a touch screen.
        mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
    } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
        // The device is a pointing device like a track pad.
        mParameters.deviceType = Parameters::DeviceType::POINTER;
    } else if (getDeviceContext().hasRelativeAxis(REL_X) ||
               getDeviceContext().hasRelativeAxis(REL_Y)) {
        // The device is a cursor device with a touch pad attached.
        // By default don't use the touch pad to move the pointer.
        mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
    } else {
        // The device is a touch pad of unknown purpose.
        mParameters.deviceType = Parameters::DeviceType::POINTER;
    }
    mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
    // 根據(jù)配置文件,決定設備類型
    String8 deviceTypeString;
    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
                                                             deviceTypeString)) {
        if (deviceTypeString == "touchScreen") {
            mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
        } else if (deviceTypeString == "touchPad") {
            mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
        } else if (deviceTypeString == "touchNavigation") {
            mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
        } else if (deviceTypeString == "pointer") {
            mParameters.deviceType = Parameters::DeviceType::POINTER;
        } else if (deviceTypeString != "default") {
            ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
        }
    }
    // 觸摸屏是方向敏感的,因此 mParameters.orientationAware 默認為 true
    mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
    // 但是,配置文件可以通過 touch.orientationAware 屬性改變 mParameters.orientationAware
    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
                                                         mParameters.orientationAware);
    mParameters.hasAssociatedDisplay = false;
    mParameters.associatedDisplayIsExternal = false;
    if (mParameters.orientationAware ||
        mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
        mParameters.deviceType == Parameters::DeviceType::POINTER) {
        // 對于觸摸屏設備,mParameters.hasAssociatedDisplay 為 true, 表示必須有關聯(lián)的顯示屏?
        mParameters.hasAssociatedDisplay = true;
        if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
            mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
            // 配置文件指定輸入設備關聯(lián)的顯示屏?
            String8 uniqueDisplayId;
            getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
                                                                 uniqueDisplayId);
            mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
        }
    }
    if (getDeviceContext().getAssociatedDisplayPort()) {
        mParameters.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.
    // 這里有一個非常有意思的事情,根據(jù)注釋所說,外部設備在觸摸時會被自動喚醒
    // 而內部設備,需要在配置文件中添加 touch.wake=true,才會在觸摸時被喚醒,但是為了防止在口袋中無觸摸喚醒設備,一般不配置這個屬性
    mParameters.wake = getDeviceContext().isExternal();
    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
}

基本參數(shù) InputDevice::mParameters 基本是由配置文件決定的,還有一部分是從驅動獲取的,這些信息應該都是“死”的。

同樣,InputDevice::mParameters 數(shù)據(jù)也是可以通過 adb shell dumpsys input 查看

  Device 5: NVTCapacitiveTouchScreen
    ....
    Touch Input Mapper (mode - DIRECT):
      Parameters:
        GestureMode: MULTI_TOUCH
        DeviceType: TOUCH_SCREEN
        AssociatedDisplay: hasAssociatedDisplay=true, isExternal=false, displayId=''
        OrientationAware: true

配置坐標系

void MultiTouchInputMapper::configureRawPointerAxes() {
    TouchInputMapper::configureRawPointerAxes();
    // 1. 獲取坐標系的信息
    // 最終調用 EventHub::getAbsoluteAxisInfo(), 詢問驅動,獲取想要的數(shù)據(jù)
    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;
        }
        // 2. 對觸摸事件的累加器進行配置
        // 對累加器 MultiTouchMotionAccumulator 進行配置,其實就是分配 Slot 數(shù)組
        // 最后一個參數(shù)表示是否使用slot協(xié)議
        mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
                                               true /*usingSlotsProtocol*/);
    } else {
        mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
                                               false /*usingSlotsProtocol*/);
    }
}

InputMapper 配置坐標系的過程如下

  • 通過 getAbsoluteAxisInfo() 從驅動獲取坐標系的信息,然后保存到 MultiTouchInputMapper::mRawPointerAxes。
  • 對觸摸事件的累加器進行配置

介紹下坐標系的信息結構,mRawPointerAxes 的類型為 RawPointerAxes,代表一個坐標系,如下

// TouchInputMapper.h
/* Raw axis information from the driver. */
struct RawPointerAxes {
    RawAbsoluteAxisInfo x;
    RawAbsoluteAxisInfo y;
    RawAbsoluteAxisInfo pressure;
    RawAbsoluteAxisInfo touchMajor;
    RawAbsoluteAxisInfo touchMinor;
    RawAbsoluteAxisInfo toolMajor;
    RawAbsoluteAxisInfo toolMinor;
    RawAbsoluteAxisInfo orientation;
    RawAbsoluteAxisInfo distance;
    RawAbsoluteAxisInfo tiltX;
    RawAbsoluteAxisInfo tiltY;
    RawAbsoluteAxisInfo trackingId;
    RawAbsoluteAxisInfo slot;
    RawPointerAxes();
    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
    void clear();
};

每一個成員變量都代表坐標系的某一種信息,但是非常有意思的事情是,所有成員變量的類型都為 RawAbsoluteAxisInfo,如下

// EventHub.h
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
    bool valid; // true if the information is valid, false otherwise
    int32_t minValue;   // minimum value
    int32_t maxValue;   // maximum value
    int32_t flat;       // center flat position, eg. flat == 8 means center is between -8 and 8
    int32_t fuzz;       // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
    int32_t resolution; // resolution in units per mm or radians per mm
    inline void clear() {
        valid = false;
        minValue = 0;
        maxValue = 0;
        flat = 0;
        fuzz = 0;
        resolution = 0;
    }
};

雖然坐標系的每一種信息都由 RawAbsoluteAxisInfo 表示,但是并不是 RawAbsoluteAxisInfo 所有的成員變量都有效,畢竟信息都有差異的。這里介紹幾個與觸摸屏相關的成員變量所代表的意思

  • RawAbsoluteAxisInfo:valid 表示坐標系是否支持此信息。
  • RawAbsoluteAxisInfo::minValue 和 RawAbsoluteAxisInfo::maxValue,它們表示 x 軸和 y 軸的坐標范圍。

同樣地,可以通過 adb shell dumpsys input 把坐標系的信息導出來

  Device 5: NVTCapacitiveTouchScreen
      ...
      Raw Touch Axes:
        X: min=0, max=719, flat=0, fuzz=0, resolution=0
        Y: min=0, max=1599, flat=0, fuzz=0, resolution=0
        Pressure: min=0, max=1000, flat=0, fuzz=0, resolution=0
        TouchMajor: min=0, max=255, flat=0, fuzz=0, resolution=0
        TouchMinor: unknown range
        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

現(xiàn)在接著看下如何對觸摸事件累加器進行配置

// 第三個參數(shù) usingSlotsProtocol 表示是否使用 slot 協(xié)議
void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
                                            bool usingSlotsProtocol) {
    mSlotCount = slotCount;
    mUsingSlotsProtocol = usingSlotsProtocol;
    mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
    delete[] mSlots;
    // 原來就是分配 Slot 數(shù)組
    mSlots = new Slot[slotCount];
}

觸摸事件累加器的配置過程非常簡單,就是創(chuàng)建 Slot 數(shù)組。當手指在觸摸屏上滑動時,Slot 數(shù)組中的每一個元素對應一個手指,保存相應手指所產(chǎn)生的一連串的坐標事件。

配置 Surface

void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
    DeviceMode oldDeviceMode = mDeviceMode;
    resolveExternalStylusPresence();
    // Determine device mode.
    if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
        // ...
    } else if (isTouchScreen()) {
        mSource = AINPUT_SOURCE_TOUCHSCREEN;
        mDeviceMode = DeviceMode::DIRECT;
        if (hasStylus()) {
            mSource |= AINPUT_SOURCE_STYLUS;
        }
        if (hasExternalStylus()) {
            mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
        }
    }
    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
        mDeviceMode = DeviceMode::DISABLED;
        return;
    }.
    std::optional<DisplayViewport> newViewport = findViewport();
    if (!newViewport) {
        mDeviceMode = DeviceMode::DISABLED;
        return;
    }
    if (!newViewport->isActive) {
        mDeviceMode = DeviceMode::DISABLED;
        return;
    }
    // Raw width and height in the natural orientation.
    int32_t rawWidth = mRawPointerAxes.getRawWidth();
    int32_t rawHeight = mRawPointerAxes.getRawHeight();
    bool viewportChanged = mViewport != *newViewport;
    bool skipViewportUpdate = false;
    if (viewportChanged) {
        bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
        mViewport = *newViewport;
        if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
            // Convert rotated viewport to natural surface coordinates.
            // 下面是根據(jù)屏幕方向,轉換坐標
            int32_t naturalLogicalWidth, naturalLogicalHeight;
            int32_t naturalPhysicalWidth, naturalPhysicalHeight;
            int32_t naturalPhysicalLeft, naturalPhysicalTop;
            int32_t naturalDeviceWidth, naturalDeviceHeight;
            switch (mViewport.orientation) {
                case DISPLAY_ORIENTATION_90:
                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                    naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
                    naturalPhysicalTop = mViewport.physicalLeft;
                    naturalDeviceWidth = mViewport.deviceHeight;
                    naturalDeviceHeight = mViewport.deviceWidth;
                    break;
                case DISPLAY_ORIENTATION_180:
                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
                    naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
                    naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
                    naturalDeviceWidth = mViewport.deviceWidth;
                    naturalDeviceHeight = mViewport.deviceHeight;
                    break;
                case DISPLAY_ORIENTATION_270:
                    naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
                    naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                    naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                    naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                    naturalPhysicalLeft = mViewport.physicalTop;
                    naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
                    naturalDeviceWidth = mViewport.deviceHeight;
                    naturalDeviceHeight = mViewport.deviceWidth;
                    break;
                case DISPLAY_ORIENTATION_0:
                default:
                    naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
                    naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
                    naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
                    naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
                    naturalPhysicalLeft = mViewport.physicalLeft;
                    naturalPhysicalTop = mViewport.physicalTop;
                    naturalDeviceWidth = mViewport.deviceWidth;
                    naturalDeviceHeight = mViewport.deviceHeight;
                    break;
            }
            if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
                ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
                naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
                naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
            }
            mPhysicalWidth = naturalPhysicalWidth;
            mPhysicalHeight = naturalPhysicalHeight;
            mPhysicalLeft = naturalPhysicalLeft;
            mPhysicalTop = naturalPhysicalTop;
            const int32_t oldSurfaceWidth = mRawSurfaceWidth;
            const int32_t oldSurfaceHeight = mRawSurfaceHeight;
            mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
            mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
            mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
            mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
            mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
            mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
            if (isPerWindowInputRotationEnabled()) {
                // When per-window input rotation is enabled, InputReader works in the un-rotated
                // 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.
                mSurfaceOrientation = mParameters.orientationAware
                        ? DISPLAY_ORIENTATION_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 = mParameters.orientationAware &&
                        mRawSurfaceWidth == oldSurfaceWidth &&
                        mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
            } else {
                mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
                                                                   : DISPLAY_ORIENTATION_0;
            }
        } else {
            // ...
        }
    }
    // If moving between pointer modes, need to reset some state.
    bool deviceModeChanged = mDeviceMode != oldDeviceMode;
    if (deviceModeChanged) {
        mOrientedRanges.clear();
    }
    // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to
    // preserve the cursor position.
    // 這里與開發(fā)者模式下的 Touch taps 功能有關,也就是顯示觸摸點
    if (mDeviceMode == DeviceMode::POINTER ||
        (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
        (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
        if (mPointerController == nullptr) {
            mPointerController = getContext()->getPointerController(getDeviceId());
        }
        if (mConfig.pointerCapture) {
            mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
        }
    } else {
        mPointerController.reset();
    }
    if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
        ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
              "display id %d",
              getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
              mSurfaceOrientation, mDeviceMode, mViewport.displayId);
        // Configure X and Y factors.
        mXScale = float(mRawSurfaceWidth) / rawWidth;
        mYScale = float(mRawSurfaceHeight) / rawHeight;
        mXTranslate = -mSurfaceLeft;
        mYTranslate = -mSurfaceTop;
        mXPrecision = 1.0f / mXScale;
        mYPrecision = 1.0f / mYScale;
        mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
        mOrientedRanges.x.source = mSource;
        mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
        mOrientedRanges.y.source = mSource;
        configureVirtualKeys();
        // Scale factor for terms that are not oriented in a particular axis.
        // If the pixels are square then xScale == yScale otherwise we fake it
        // by choosing an average.
        mGeometricScale = avg(mXScale, mYScale);
        // Size of diagonal axis.
        float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
        // Size factors.
        // ...
        // Pressure factors.
        // ...
        // Tilt
        // ...
        // Orientation
        // ...
        // Distance
        // ...
        // Location
        updateAffineTransformation();
        if (mDeviceMode == DeviceMode::POINTER) {
            // ...
        }
        // Inform the dispatcher about the changes.
        *outResetNeeded = true;
        bumpGeneration();
    }
}

這些信息太復雜了,不過還是可以通過 adb shell dupmsys input 導出來

 Device 5: NVTCapacitiveTouchScreen
    ...
    Touch Input Mapper (mode - DIRECT):
      ....
      Viewport INTERNAL: displayId=0, uniqueId=local:4630946979286703745, port=129, orientation=0, logicalFrame=[0, 0, 720, 1600], physicalFrame=[0, 0, 720, 1600], deviceSize=[720, 1600], isActive=[1]
      RawSurfaceWidth: 720px
      RawSurfaceHeight: 1600px
      SurfaceLeft: 0
      SurfaceTop: 0
      SurfaceRight: 720
      SurfaceBottom: 1600
      PhysicalWidth: 720px
      PhysicalHeight: 1600px
      PhysicalLeft: 0
      PhysicalTop: 0
      SurfaceOrientation: 0
      Translation and Scaling Factors:
        XTranslate: 0.000
        YTranslate: 0.000
        XScale: 1.000
        YScale: 1.000
        XPrecision: 1.000
        YPrecision: 1.000
        GeometricScale: 1.000
        PressureScale: 0.001
        SizeScale: 0.004
        OrientationScale: 0.000
        DistanceScale: 0.000
        HaveTilt: false
        TiltXCenter: 0.000
        TiltXScale: 0.000
        TiltYCenter: 0.000
        TiltYScale: 0.000

小結

設備的掃描與合成事件的處理過程如下

  • EventHub 創(chuàng)建并保存輸入設備 Deivce 到 mDevices 中,其中包括為輸入設備加載配置文件。
  • InputReader 創(chuàng)建并保存輸入設備 InputDevice 到 mDevices 中,其中包括對 InputDevice 進行配置,對 InputDevice 所保存的 InputMapper 進行配置。

設備的掃描與合成事件的處理過程并不復雜,但是對輸入設備是極其繁瑣的,這些配置數(shù)據(jù)到底有何用,我們在后面分析輸入事件的處理過程中,再見分曉。

以上就是Input系統(tǒng)之InputReader處理合成事件詳解的詳細內容,更多關于InputReader處理合成事件的資料請關注腳本之家其它相關文章!

相關文章

  • DownloadManager實現(xiàn)文件下載功能

    DownloadManager實現(xiàn)文件下載功能

    這篇文章主要為大家詳細介紹了DownloadManager實現(xiàn)文件下載功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Android數(shù)據(jù)持久化之讀寫SD卡中內容的方法詳解

    Android數(shù)據(jù)持久化之讀寫SD卡中內容的方法詳解

    這篇文章主要介紹了Android數(shù)據(jù)持久化之讀寫SD卡中內容的方法,結合具體實例形式分析了Android持久化操作中針對SD卡進行讀寫操作的相關實現(xiàn)技巧與注意事項,需要的朋友可以參考下
    2017-05-05
  • Kotlin語言使用WebView示例介紹

    Kotlin語言使用WebView示例介紹

    隨著后臺技術的不斷發(fā)展,App前端的應用都布置了Web頁面的界面,這個界面就是由WebView組件渲染出來的。WebView由如下優(yōu)點:可以直接顯示和渲染W(wǎng)eb頁面或者網(wǎng)頁;可以直接調用網(wǎng)絡上或者本地的html文件,也可以和JavaScript交互使用
    2022-09-09
  • Flutter系統(tǒng)網(wǎng)絡圖片加載流程解析

    Flutter系統(tǒng)網(wǎng)絡圖片加載流程解析

    這篇文章主要介紹了Flutter系統(tǒng)網(wǎng)絡圖片加載流程,從構造函數(shù)開始說起,我們以最簡單的調用方式舉例,當我們使用Image.network(imageUrl)這種方式來顯示圖片時,Image組件內部image屬性就會被賦值NetworkImage,具體操作步驟跟隨小編一起看看吧
    2022-05-05
  • Android中FlowLayout組件實現(xiàn)瀑布流效果

    Android中FlowLayout組件實現(xiàn)瀑布流效果

    大家好,本篇文章主要講的是Android中FlowLayout組件實現(xiàn)瀑布流效果,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Android如何快速適配暗黑模式詳解

    Android如何快速適配暗黑模式詳解

    微信在前段時間的更新中也實現(xiàn)了暗黑模式,而蘋果系統(tǒng)也早就支持暗黑模式,Android也一樣可以實現(xiàn),下面這篇文章主要給大家介紹了關于Android如何快速適配暗黑模式的相關資料,需要的朋友可以參考下
    2021-08-08
  • Android實現(xiàn)的數(shù)字格式化用法示例

    Android實現(xiàn)的數(shù)字格式化用法示例

    這篇文章主要介紹了Android實現(xiàn)的數(shù)字格式化用法,結合實例形式分析了Android數(shù)學運算中數(shù)字格式化輸出的相關技巧,需要的朋友可以參考下
    2016-08-08
  • Android FTP 多線程斷點續(xù)傳下載\上傳的實例

    Android FTP 多線程斷點續(xù)傳下載\上傳的實例

    本篇文章主要介紹了Android FTP 多線程斷點續(xù)傳下載\上傳的實例,具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • 淺談關于android軟鍵盤彈出問題

    淺談關于android軟鍵盤彈出問題

    本篇文章主要介紹了淺談關于android軟鍵盤彈出問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • android TabLayout使用方法詳解

    android TabLayout使用方法詳解

    這篇文章主要為大家詳細介紹了android TabLayout使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08

最新評論