Android源码笔记-输入事件(二)

        这一节主要了解输入事件的获取,InputReaderThread继承自C++的Thread类,Thread类封装了pthread线程工具,提供了与Java层Thread类相似的API。C++的Thread类提供了一个名为threadLoop()的纯虚函数,当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程。

/frameworks/native/services/inputflinger/InputThread.cpp

class InputThreadImpl : public Thread {
public:
    explicit InputThreadImpl(std::function<void()> loop)
          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
    ~InputThreadImpl() {}

private:
    std::function<void()> mThreadLoop;
    bool threadLoop() override {
        mThreadLoop();
        return true;
    }
};
/frameworks/native/services/inputflinger/reader/InputReader.cpp

   status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        AutoMutex _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

    // 通过EventHub获取事件列表 读取的结果存储在参数mEventBuffer中 表示事件的个数
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

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

		//如果获取到事件 对事件进行加工处理
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
	// 发布事件 将所有事件交给InputDispatcher
    mQueuedListener->flush();
}

分析:首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。通过 processEventsLocke()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener 中。所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

  /frameworks/native/services/inputflinger/reader/EventHub.cpp
   
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
    AutoMutex _l(mLock);
    struct input_event readBuffer[bufferSize];
	//每存储一个事件 event指针会向后偏移一个元素
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // 重新打开设备节点
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;

            ALOGI("Reopening all input devices due to a configuration change.");

            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }
      ...

        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

        while (mOpeningDevices != nullptr) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
        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;
		// 处理未被InputReader取走的输入事件与设备事件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.fd == mINotifyFd) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.fd == mWakeReadPipeFd) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                          eventItem.events);
                }
                continue;
            }

            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            if (!device) {
                ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
                      eventItem.data.fd);
                ALOG_ASSERT(!DEBUG);
                continue;
            }
            if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
                if (eventItem.events & EPOLLIN) {
                    size_t numFrames = device->videoDevice->readAndQueueFrames();
                    if (numFrames == 0) {
                        ALOGE("Received epoll event for video device %s, but could not read frame",
                              device->videoDevice->getName().c_str());
                    }
                } else if (eventItem.events & EPOLLHUP) {
                    // TODO(b/121395353) - consider adding EPOLLRDHUP
                    ALOGI("Removing video device %s due to epoll hang-up event.",
                          device->videoDevice->getName().c_str());
                    unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
                    device->videoDevice = nullptr;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
                          device->videoDevice->getName().c_str());
                    ALOG_ASSERT(!DEBUG);
                }
                continue;
            }
            // This must be an input event
            if (eventItem.events & EPOLLIN) {
                int32_t readSize =
                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                ...
        }

		// 设备节点发生增删操作
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }
		// 等待新的事件
        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) {
            // An error occurred.
            mPendingEventCount = 0;

            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }
    // 获取所读取的事件数量
    return event - buffer;
}

分析: getEvent()函数使用Epoll的核心是mPendingEventItems数组,它是一个事件池。getEvents()函数会优先从这个事件池获取epoll 事件进行处理,并将读取相应的原始输入事件返回给调用者。当无法获得任何事件时,会调用epoll_wait()函数等待新事件到来,将事件池重新注满,然后再重新处理事件池中的Epoll事件。

EventHub的直译是事件集线器,它将所有的输入事件通过一个接口 getEvents()把从多个输人设备节点中读取的事件交给InputReader,它是输人系统最底层的一个组件。 

/frameworks/native/services/inputflinger/reader/InputReader.cpp   
	
	void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
		// 根据事件的类型区分原始输入事件或设备增删事件
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
      // 处理属于某一设备的一批事件 batchSize表示属于此设备的输入事件的个数
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

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

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

	// 调用InputDevice的process函数进行处理
    device->process(rawEvents, count);
}
/frameworks/native/services/inputflinger/reader/InputDevice.cpp
 
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
  
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
              rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
#endif

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
                ALOGD("Recovered from input event buffer overrun.");
#endif
            } else {
#if DEBUG_RAW_EVENTS
                ALOGD("Dropped input event while waiting for next input sync.");
#endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
}	

分析:InputDevice用来存储输入设备的信息,事件交给InputMapper的process()事件处理,它有很多子类 用于加工不同类型的原始输入事件,这里有点像责任连模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/521769.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux实践室】Linux高级用户管理实战指南:创建与删除用户组操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux创建用户组命令2.1.1 知识点讲解2.1.2…

Arduino的OTA在线升级

一、OTA 介绍 OTA是Over-the-Air的缩写&#xff0c;中文意思是空中下载技术。通过移动通信&#xff08;GSM或CDMA&#xff09;的空中接口对SIM卡数据及应用进行远程管理的技术。空中接口可以采用WAP、GPRS、CDMA1X及短消息技术。OTA技术的应用&#xff0c;使得移动通信不仅可以…

读所罗门的密码笔记12_群雄逐鹿(上)

1. 国际电信规则 1.1. 美国坚持互联网自由和极少的内容限制&#xff0c;这一立场肯定会遭到许多国家的反对 1.2. 除去两个各方针锋相对、无法妥协的议题&#xff0c;比如内容限制规定&#xff0c;实际上所有国家都已在打击垃圾邮件和常见网络安全威胁方…

Windows Server 2012 R2安装Web服务器IIS

文章目录 一、打开【服务器管理器】二、点击【添加角色和功能】三、点击【下一步】四、点击【下一步】五、点击【下一步】六、勾选【Web服务器(IIS)】→点击【添加功能】→点击【下一步】七、勾选【.NET Framework 3.5 功能】→点击【下一步】八、点击【下一步】九、点击【下一…

基于H2O AutoML与集成学习策略的房屋售价预测模型研究与实现

项目简述&#xff1a; 本项目采用H2O AutoML工具&#xff0c;针对加州房屋销售价格预测问题进行了深入研究与建模。项目以Kaggle提供的加州房屋 交易数据集为基础&#xff0c;通过数据清洗、特征工程、模型训练与评估等步骤&#xff0c;构建了一种基于集成学习策略的房价预测模…

LeetCode刷题记(二):31~60题

31. 下一个排列 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下这些都可以视作 arr 的排列&#xff1a;[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。…

如何确认RID池是否耗尽,以及手动增加RID池大小

确认RID池是否耗尽&#xff1a; 事件查看器&#xff1a; 在RID主控域控制器上打开事件查看器&#xff0c;导航至“Windows日志 > 应用程序和服务日志 > Microsoft > Windows > Directory Service > Operations”。搜索事件ID 16656和16657。事件ID 16656表明RID…

C++超高精度计时器

#include <chrono> #include <QDebug> #pragma execution_character_set("utf-8") /*1 秒&#xff08;s&#xff09; 1,000 毫秒&#xff08;ms&#xff09; 1,000,000 微秒&#xff08;μs&#xff09; 1,000,000,000 纳秒&#xff08;ns&#xff09…

Leetcode 第 389 场周赛题解

Leetcode 第 389 场周赛题解 Leetcode 第 389 场周赛题解题目1&#xff1a;3083. 字符串及其反转中是否存在同一子字符串思路代码复杂度分析 题目2&#xff1a;3084. 统计以给定字符开头和结尾的子字符串总数思路代码复杂度分析 题目3&#xff1a;3085. 成为 K 特殊字符串需要删…

Fire Smoke - Dynamic Nature

烟雾、火灾和爆炸预制件、着色器的集合。粒子支持HD、URP和标准渲染,自然制造风,因此它们对风速、方向和颤抖做出反应。 包装支持: Unity 2021.2及更高版本 Unity 2021.2 HD RP Unity 2021.2 URP Unity 2021.3及更高版本 Unity 2021.3 LTS HD RP Unity 2021.3 LTS URP Unity…

202112青少年软件编程(Scratch图形化)等级考试试卷(四级)

第1题&#xff1a;【 单选题】 小猫和小狗是非常好的朋友&#xff0c; 他们发明了一种加密方法&#xff1a; 用两位数字代表字母。比如 65 代表 A&#xff0c; 66 代表 B……&#xff0c; 75 代表 K&#xff0c; ……&#xff0c; 78 代表 N&#xff0c; 79 代表 O、 80 代表 …

zdpdjango_argonadmin使用Django开发一个美观的后台管理系统

初始代码 安装依赖 pip install -r requirements.txt生成管理员账户 迁移模型&#xff1a; python manage.py makemigrations python manage.py migrate创建超级用户&#xff1a; python manage.py createsuperuser启动服务 python manage.py runserver浏览器访问&#xf…

ChatGPT官宣新增Dynamic(动态)模式!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

Java中IO流详解

文章目录 字符流问题导入编码表**出现乱码的原因**ASCII表Unicode表汉字存储和展示过程解析问题导入解答 介绍分类字符输出流字符输入流字符缓冲流 特殊操作流转化流对象操作流打印流 工具包Commons-io介绍分类IOUtils类FileUtils类 字符流 问题导入 既然字节流能操作所有文件…

探索Linux的挂载操作

在Linux这个强大的操作系统中&#xff0c;挂载操作是一个基本而重要的概念。它涉及到文件系统、设备和数据访问&#xff0c;对于理解Linux的工作方式至关重要。那么&#xff0c;挂载操作究竟是什么&#xff0c;为什么我们需要它&#xff0c;如果没有它&#xff0c;我们将面临什…

递归学习第一个课

一、递归定义 基本定义 函数自己调用自己&#xff08;通俗第一印象&#xff09;大问题可以拆分小问题&#xff08;拆分&#xff0c;边界&#xff09;大问题与小问题的关系&#xff08;递归关系&#xff09; 为什么拆分小问题&#xff1f; 小问题更容易求解大问题与小问题内部…

基于Spark中随机森林模型的天气预测系统

基于Spark中随机森林模型的天气预测系统 在这篇文章中&#xff0c;我们将探讨如何使用Apache Spark和随机森林算法来构建一个天气预测系统。该系统将利用历史天气数据&#xff0c;通过机器学习模型预测未来的天气情况&#xff0c;特别是针对是否下雨的二元分类问题。 简介 Ap…

SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型

SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型 文章目录 SpringBoot3整合RabbitMQ之四_发布订阅模型中的fanout模型3. 发布/订阅模型之fanout模型1. 说明1. 消息发布者1. 创建工作队列的配置类2. 发布消费Controller 2. 消息消费者One3. 消息消费者Two4. 消息消费者…

实际项目中如何使用Git做分支管理

前言 Git是一种强大的分布式版本控制系统&#xff0c;在实际项目开发中使用Git进行分支管理是非常常见的做法&#xff0c;因为它可以帮助团队高效的协作和管理项目的不同版本&#xff0c;今天我们来讲讲在实际项目中最常用的Git分支管理策略Git Flow。 常见的Git分支管理策略…

IDEA2024.1版本震撼来袭,手把手教你激活!

前言 作为一个Java程序猿&#xff0c;必不可少的一款开发IDE神器&#xff1a;IntelliJ IDEA&#xff0c;简称“IDEA”。就在前天&#xff08;2024.4.4&#xff09;终于推出了心心念念的2024.1版本。 IntelliJ IDEA 2024.1 引入了一系列令人期待的升级&#xff0c;可以帮助您简…