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

news2024/11/18 4:44:35

        这一节主要了解输入事件的获取,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()事件处理,它有很多子类 用于加工不同类型的原始输入事件,这里有点像责任连模式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】软硬链接 / 动静态库

目录 一. 软硬链接1. 硬链接2. 软链接3. unlink4. 目录的硬链接 二. 动静态库1.1 静态库制作1.2 静态库使用2.1 动态库制作2.2 动态库使用3. 动态链接原理 一. 软硬链接 1. 硬链接 硬链接(hard link) 可以将它理解为原始文件的别名, 和原始文件使用相同的 inode 编号和 data …

GraphSage

背景 大型图中节点的低维嵌入在各种预测任务中非常有用。GraphSage是一种通用的归纳框架&#xff0c;它利用节点特征信息&#xff08;例如&#xff0c;文本属性&#xff09;有效地为以前看不见的数据生成节点嵌入。相比于对每个节点训练单独的嵌入&#xff0c;GraphSage学习了一…

深入浅出 -- 系统架构之单体到分布式架构的演变

一、传统模式的技术改革 在很多年以前&#xff0c;其实没有严格意义上的前后端工程师之分&#xff0c;每个后端就是前端&#xff0c;同理&#xff0c;前端也可以是后端&#xff0c;即Ajax、jQuery技术未盛行前的年代。 起初&#xff0c;大部分前端界面很简单&#xff0c;显示的…

thinkphp6入门(21)-- 如何删除图片、文件

假设文件的位置在 /*** 删除文件* $file_name avatar/20240208/d71d108bc1086b498df5191f9f925db3.jpg*/ function deleteFile($file_name) {// 要删除的文件路径$file app()->getRootPath() . public/uploads/ . $file_name; $result [];if (is_file($file)) {if (unlin…

【语音识别】声学建模中基于树的状态绑定

01 基本想法 单音素HMM模型不能很好的应对自然说话人发音时的渐变过程&#xff0c;比如从一个音素转换到另一个音素时会存在协同发音现象。因此语音识别的先驱者提出了上下文建模概念&#xff0c;即使用中心音素&#xff08;单因素&#xff09;和前后两个音素组成三音素对每一…

【Laravel】09 用模型批量赋值简化代码 数据库关系

【Laravel】09 用模型批量赋值简化代码 & 数据库关系 1. 用模型批量赋值简化代码2. 数据库关系 1. 用模型批量赋值简化代码 原来存储一个值 2. 数据库关系 这里可以看到两个SQL是一样的

函数重载和引用【C++】

文章目录 函数重载什么是函数重载&#xff1f;函数重载的作用使用函数重载的注意点为什么C可以函数重载&#xff0c;C语言不行&#xff1f; 引用什么是引用&#xff1f;引用的语法引用的特点引用的使用场景引用的底层实现传参时传引用和传值的效率引用和指针的区别 函数重载 什…

QT Creator概览

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、Qt Creator 概览 ①&#xff1a;菜单栏 ②&#xff1a;模式选择 ③&#xff1a;构建套件选择器…

Vue2电商前台项目(二):完成Home首页模块业务

一、项目开发的步骤 1、书写静态页面&#xff08;HTML&#xff0c;CSS&#xff09; 2、拆分组件 3、获取服务器的数据动态展示 4、完成相应的动态业务逻辑 经过分析之后&#xff0c;Home首页可以拆分为7个组件&#xff1a;TypeNav三级联动导航&#xff0c;ListContainer&…

深入解析:如何使用Xcode上传苹果IPA安装包至App Store?

目录 引言 摘要 第二步&#xff1a;打开appuploader工具 第二步&#xff1a;打开appuploader工具&#xff0c;第二步&#xff1a;打开appuploader工具 第五步&#xff1a;交付应用程序&#xff0c;在iTunes Connect中查看应用程序 总结 引言 在将应用程序上架到苹果应用商…

【保姆级讲解如何安装与配置Node.js】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Arduino开发 esp32cam+opencv人脸识别距离+语音提醒

效果图 低于20厘米语音提醒字体变红 Arduino代码 可直接复制使用&#xff08;修改自己的WIFI) #include <esp32cam.h> #include <WebServer.h> #include <WiFi.h> // 设置要连接的WiFi名称和密码 const char* WIFI_SSID "gumou"; const char* …

【C语言】if语句选择题

前言 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目八&#xff1a; 前言 关于if语句相关的选择题 题目一&#xff1a; 关于if语句说法正确是&#xff1a;( ) A .if语…

[StartingPoint][Tier1]Crocodile

Task 1 What Nmap scanning switch employs the use of default scripts during a scan? (哪些 Nmap 扫描开关在扫描期间使用默认脚本&#xff1f;) -sC Task 2 What service version is found to be running on port 21? 发现端口 21 上运行的服务版本是什么&#xff1f…

隐私计算实训营学习七:隐语SCQL的架构详细拆解

文章目录 一、SCQL Overview1.1 SCQL背景1.2 SCQL Overview 二、SCQL CCL三、SCQL架构 一、SCQL Overview 1.1 SCQL背景 SCQL&#xff1a;属于隐私计算BI范畴&#xff0c;允许多个互不信任参与方在不泄露各自隐私数据的条件下进行联合数据分析。 如下数据在不同机构&#xf…

CCleaner如何还原系统 CCleaner怎么恢复注册表 ccleaner官方下载

CCleaner是一款电脑清理软件&#xff0c;其中的注册表清理功能是该软件很重要的功能。注册表作为电脑的重要文件&#xff0c;不可以随便清理&#xff0c;而CCleaner可以帮我们安全&#xff0c;快速地清除注册表。同时&#xff0c;CCleaner还有还原系统的功能。下面将为大家介绍…

ST表(Segment Tree)

目录 1.概述 2.引入 3.ST表对引入的优化 1.概述 ST表是一种基于树形结构的数据结构&#xff0c;用于处理区间查询和更新操作。它通过预处理的方式将原始数据存储在树状结构中&#xff0c;以支持高效的区间查询。ST表的构建时间复杂度为O(nlogn)&#xff0c;其中n为原始数据…

07 | Swoole 源码分析之 Channel 通道模块

原文首发链接&#xff1a;Swoole 源码分析之 Channel 通道模块 大家好&#xff0c;我是码农先森。 引言 通道&#xff0c;用于协程间通讯&#xff0c;支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。 通道与 PHP 的 Array 类似&#xff0c;仅占用内存&am…

非关系型数据库(缓存数据库)redis的基础认知与安装

目录 一.关系型数据库和非关系型数据库 关系型数据库 非关系型数据库 关系数据库与非关系型数据库的区别 ①非关系数据 关系型数据库 非关系型数据库产生背景 数据存储流向 非关系型数据库 关系数据库 二.redis的简介 1.概念 2.Redis 具有以下几个优点: 3.Redi…

Salesforce团队:科技文档的受众比你想象要多

▲ 搜索“大龙谈智能内容”关注公众号▲ 扫码见我视频号上的视频 视频时长为38分钟&#xff0c;如果你没有时间看&#xff0c;这里是我用AI做的总结&#xff1a; 视频总结 Salesforce的文档团队成员讨论了文档在销售过程中对潜在客户的重要性。 集锦 00:48 &#x1f4a1; S…