InputFilnger

0. 关键字

InputFilnger, InputManager, InputReader, InputDispatcher

1.参考文档

参考1:参考网址
参考2:Android Input系统简介
参考3:Android Framework 输入子系统(05)InputDispatcher解读

2.InputManager的初始化

  • 2.1 InputManagerService在systemserver进程中被启动
//frameworks\base\services\java\com\android\server\SystemServer.java// 1.构造inputManager = new InputManagerService(context);// 2 注册服务ServiceManager.addService(Context.INPUT_SERVICE, inputManager,/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);// 3inputManager.start();
  • 2.2 初始化的内容:
  • 2.2.1 InputManagerService的构造函数
    调用到nativeInit(), 会通过JNI调用到C/C++的Java_com_android_server_InputManagerService_nativeInit
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == nullptr) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());  //  1im->incStrong(0);return reinterpret_cast<jlong>(im);
}static const JNINativeMethod gInputManagerMethods[] = {/* name, signature, funcPtr */{"nativeInit","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/""MessageQueue;)J",(void*)nativeInit},//省略

3. new NativeInputManager的初始化

3.1 InputReader部分

3.1.1 InputReader的初始化----createInputReader

sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {return new InputReader(std::make_unique<EventHub>(), policy, listener);  // 
}
// 启动InputReader的线程
mReader = createInputReader(readerPolicy, mClassifier);
mReader->start();   // 调用到 InputReader::start

3.1.2 InputReader::start


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

创建线程"InputReader"—执行loopOnce函数

3.1.3 InputReader::loopOnce执行 getEvent函数来获取输入事件

void InputReader::loopOnce() {
。。。。。size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);  //  
。。。。。
}

3.1.4 调用到EventHub::getEvents

主要就是下面的部分: 循环的拿到传上来的事件,

......int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);  // 等待fdSet关注的Fd事件
.......if (eventItem.events & EPOLLIN) {  // 处理常规输入事件{  // 拿到正常的输入事件int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;size_t count = size_t(readSize) / sizeof(struct input_event);for (size_t i = 0; i < count; i++) {  // 逐帧读取RawEventstruct input_event& iev = readBuffer[i];event->when = processEventTimestamp(iev);event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);event->deviceId = deviceId;event->type = iev.type;event->code = iev.code;event->value = iev.value;event += 1;capacity -= 1;}........

3.1.5 InputReader::loopOnce()中的 processEventsLocked

调用到 InputReader::processEventsForDeviceLocked
---->调用到 device->process(rawEvents, count);

3.1.6 调用到InputDevice::process

关键是调用下面的mapper.process, mapper是InputMapper类型变量。
会找到触摸屏对应的devices—TouchInputMapper

            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {mapper.process(rawEvent);   // InputMapper 执行process操作,// InputMapper是基类----找到比如MouseInputMapper(鼠标输入处理类)// TouchInputMapper 是触摸屏

3.1.7 TouchInputMapper::process 会处理点击事件

sync会最终调用到processRawTouches, 会调用到cookAndDispatch, 最后调用到dispatchTouches将本次的触摸事件传递给InputDispater线程。

void TouchInputMapper::process(const RawEvent* rawEvent) {  // 处理点击事件mCursorButtonAccumulator.process(rawEvent);  // "鼠标滑动"事件mCursorScrollAccumulator.process(rawEvent);  // "鼠标滚轮"事件mTouchButtonAccumulator.process(rawEvent);  //  "鼠标点击"事件if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {sync(rawEvent->when, rawEvent->readTime);}
}

3.1.8 dispatchTouches

getListener()->notifyMotion(&args);
// class InputDispatcher : public android::InputDispatcherInterface,
// InputDispatcherInterface 继承 InputListenerInterface
// getListener()->notifyMotion最终对应到InputDispatcher::notifyMotion方法。void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {。。。。。needWake = enqueueInboundEventLocked(std::move(newEntry));}
  • enqueueInboundEventLocked最终会将数据写入mInboundQueue队列

3.1.9 InputReader的关键流程:

  • open设备节点**/dev/input**, 当有输入事件,则会写入/dev/input
  • 通过InputManager实例化InputReader
  • 然后InputReader::start来启动loopOnce()线程
  • loopOnce()线程首先会在循环中在epoll_wait中拿到关注的**/dev/input**的相关事件。
  • loopOnce()线程会通过EventHub::getEvent方法来读取"Input事件",
  • loopOnce()线程通过processEventLocked(), 在通过层层调用,调用到sync,把事件最终传递给InputDispater.

3.2 InputDispater部分

3.2.1 InputDispater的初始化----createInputReader

InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {mDispatcher = createInputDispatcher(dispatcherPolicy);
.......
}
// 启动InputDispatcher线程status_t result = mDispatcher->start();

3.2.1 启动InputDispatcher线程

  • InputDispatcher::start()
    创建名为"InputDispatcher"的线程,调用dispatchOnce()函数
status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}

3.2.3 InputDispatcher::dispatchOnce()

 
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;......if (!haveCommandsLocked()) {//判断CommandQueue是否有命令dispatchOnceInnerLocked(&nextWakeupTime);}
..........mLooper->pollOnce(timeoutMillis); 
}
  • 首先 pollOnce阻塞等待InputReader调用wake()唤醒
  • 调用到dispatchOnceInnerLocked来分发事件

3.2.4 dispatchOnceInnerLocked

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
....................if (!mPendingEvent) {//当InputReader队列中插入一个输入事件后,此处mInboundQueue不为空if (mInboundQueue.empty()) {.................} else {  // 拿到InputReader写入mInboundQueue 队列的EventmPendingEvent = mInboundQueue.front(); // 从 mInboundQueue 队列取出一条事件.......}}//...switch (mPendingEvent->type) {//...case EventEntry::TYPE_KEY: {KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);...// 分发按键事件done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);break;}}

3.2.5 dispatchKeyLocked

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {
..........// Identify targets.Vector<InputTarget> inputTargets;//找到当前激活的Window窗口,并将其加入到inputTargets中int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime);//将按键分发到上面Vendor的InputChannel中dispatchEventLocked(currentTime, entry, inputTargets);return true;
}

3.2.6 dispatchEventLocked

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {pokeUserActivityLocked(eventEntry);// 遍历inputTargetsfor (size_t i = 0; i < inputTargets.size(); i++) {const InputTarget& inputTarget = inputTargets.itemAt(i);ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);if (connectionIndex >= 0) {sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);// 分发prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);}}
}
  • 遍历inputTargets,找到connection,调用prepareDispatchCycleLocked进行分发

3.2.7 prepareDispatchCycleLocked

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
......enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
...............// If the outbound queue was previously empty, start the dispatch cycle going.if (wasEmpty && !connection->outboundQueue.isEmpty()) {startDispatchCycleLocked(currentTime, connection);}
}

3.2.8 startDispatchCycleLocked

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {  // 
.........while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {// 从connection->outboundQueue取出事件DispatchEntry* dispatchEntry = connection->outboundQueue.front();dispatchEntry->deliveryTime = currentTime;const std::chrono::nanoseconds timeout =getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());dispatchEntry->timeoutTime = currentTime + timeout.count();// Publish the event.status_t status;const EventEntry& eventEntry = *(dispatchEntry->eventEntry);switch (eventEntry.type) {case EventEntry::Type::KEY: {const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);// Publish the key event.publishKeyEvent完成分发事件status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId, keyEntry.deviceId,keyEntry.source, keyEntry.displayId,std::move(hmac), dispatchEntry->resolvedAction,dispatchEntry->resolvedFlags, keyEntry.keyCode,keyEntry.scanCode, keyEntry.metaState,keyEntry.repeatCount, keyEntry.downTime,keyEntry.eventTime);break;}

3.2.9 publishKeyEvent

status_t InputPublisher::publishKeyEvent(uint32_t seq,//...nsecs_t downTime,nsecs_t eventTime) {//...InputMessage msg;msg.header.type = InputMessage::TYPE_KEY;//...msg.body.key.eventTime = eventTime;return mChannel->sendMessage(&msg);  // 对应InputChannel::sendMessage
}

3.2.10 sendMessage

status_t InputChannel::sendMessage(const InputMessage* msg) {size_t msgLength = msg->size();ssize_t nWrite;do {nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);//...if (size_t(nWrite) != msgLength) {return DEAD_OBJECT;}return OK;
}
  • 最终是调用的socket的send方法来发送到对应的APP

3.2.11 总结

  • InputReader调用getListener()->notifyMotion()发送消息到InputDispatcher(本质上是写入mInboundQueue队列
  • InputDispatcher在InputManager被初始化,调用了InputDispatcher::start方法来启动一个名为“InputDispatcher”的线程,并循环调用InputManager::dispatchOnce方法
  • pollOnce阻塞等待消息,然后拿到消息后。调用dispatchOnceInnerLocked
  • 以按键事件为例,会调用到dispatchKeyLocked
  • 一步步调用到publishKeyEvent将InputMessage, 通过socket发送到server端。

4. InputEventReceiver

  • 主要就是WMS中的mInputEventReceiver作为接收线程,接收来自InputDispatcher的消息。
>frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {mInputChannel = new InputChannel();}try {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // 将mInputChannel 注册到WMS中} catch (RemoteException e) {...}if (mInputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}// 创建app端监听,即WindowInputEventReceiver 作为事件的接收端mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}}

5. 点击屏幕到响应这个流程是怎样的?

  1. 首先点击了之后,会被硬件捕获,然后此事件被输入到驱动程序,并传递到Android系统。
    通过adb shell getevent,监测**/dev/input**目录来看linux是否传递了点击事件上来。
    然后点击屏幕,会发现有点击事件。
  2. 输入事件传入InputReader
  3. InputReader将消息转给InputDispatcher
  4. WMS 中的InputEventReceiver会收到InputDispatcher发送出来的消息。

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

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

相关文章

PH热榜 | 2024-11-12

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 在线预览 1. Spiky 标语&#xff1a;实时洞察&#xff0c;助力销售决策更快更明智。 介绍&#xff1a;Spiky AI 帮你用实时指导提升团…

C++ 关于基于范围的for循环(C++11) 语法 详解

范围for的语法 在 C98 中如果要遍历一个数组 void TestFor() { int array[] { 1, 2, 3, 4, 5 }; for (int i 0; i < sizeof(array) / sizeof(array[0]); i)array[i] * 2; for (int* p array; p < array sizeof(array)/ sizeof(array[0]); p)cout << *p <<…

【入门篇】判断推理是否有效的实例2——多语言版

跳转原题&#xff1a;判断推理是否有效的实例2 问题分析 根据题目给出的推理逻辑&#xff0c;我们有以下几个条件&#xff1a; 如果张老师来了&#xff08;(P)&#xff09;&#xff0c;问题可以解答&#xff08;(R)&#xff09;&#xff1a;(P \rightarrow R)如果李老师来了&…

5GAP模型:探寻服务质量问题的产生源头

| 91%的消费者表示&#xff0c;他们更有可能在获得卓越的服务体验后再次购买——Salesforce Research 一、什么是5GAP模型&#xff1f; 5GAP模型&#xff0c;指的是服务质量差距模型&#xff08;Service Quality Model&#xff09;&#xff0c;它是由美国营销学家帕拉休拉曼、…

期刊论文查重率多少,才会不被认定为学术不端?

Q问&#xff1a;论文查重和学术不端具有紧密的相关性&#xff0c;但是被认定为学术不端的查重率的界限是什么&#xff1f; A答&#xff1a;关于论文和查重&#xff0c;虽然这两者之间有着“说不清也道不明”的关系&#xff0c;这其中很重要的一个原因是很多人对查重都有一定的…

JAVA中重写与重载的极简区别

重载就是同样的一个方法能够根据输入数据的不同&#xff0c;做出不同的处理重写就是当子类继承自父类的相同方法&#xff0c;输入数据一样&#xff0c;但要做出有别于父类的响应时&#xff0c;你就要覆盖父类方法 方法的重写(Overriding)和重载(Overloading)是java多态性的不同…

[Linux]:IO多路转接之epoll

1. IO 多路转接之epoll 1.1 epoll概述 epoll是Linux内核为处理大规模并发网络连接而设计的高效I/O多路转接技术。它基于事件驱动模型&#xff0c;通过在内核中维护一个事件表&#xff0c;能够快速响应多个文件描述符上的I/O事件&#xff0c;如可读、可写、异常等&#xff0c;…

从0开始学习Linux——用户管理

往期目录&#xff1a; 从0开始学习Linux——简介&安装 从0开始学习Linux——搭建属于自己的Linux虚拟机 从0开始学习Linux——文本编辑器 从0开始学习Linux——Yum工具 从0开始学习Linux——远程连接工具 从0开始学习Linux——文件目录 从0开始学习Linux——网络配置 从0开…

Docker 安装Immich教程

Immich 是一个开源的自托管照片和视频管理平台,专为帮助用户存储、管理、和分享个人媒体库而设计。Immich 的目标是提供一个类似 Google Photos 的替代方案,但不依赖于第三方服务,用户可以完全控制自己的数据。 本章教程,记录如何用Docker部署安装Immich,使用的操作系统的…

回首遥望-C++内存对齐的思考

这一章节主要巩固一下学习C/C时内存对齐相关的内容&#xff01; 文章目录 什么是内存对齐&#xff1f;为什么要有内存对齐&#xff1f;如何进行内存对齐&#xff1f;致谢&#xff1a; 什么是内存对齐&#xff1f; 这里不提及一堆啰嗦概念&#xff0c;就结合实际出发&#xff0…

从代码到云端:使用PyCharm打包Python项目并部署至服务器的完整指南

从代码到云端&#xff1a;使用PyCharm打包Python项目并部署至服务器的完整指南 引言 随着云计算技术的发展&#xff0c;越来越多的开发者选择将自己的应用部署到云服务器上。对于Python开发人员来说&#xff0c;如何高效地将本地开发完成的应用程序迁移到远程服务器成为了一个…

【ComfyUI +BrushNet+PowerPaint】图像修复(根据题词填充目标)——ComfyUI-BrushNet

运行代码&#xff1a;https://github.com/nullquant/ComfyUI-BrushNet 源码1&#xff1a;https://github.com/TencentARC/BrushNet 源码2&#xff1a;https://github.com/open-mmlab/PowerPaint 上图&#xff0c;中间未 random_mask.safetensors结果&#xff0c;最右边图未segm…

(时序论文阅读)TimeMixer: Decomposable Multiscale Mixing for Time Series Forecasting

来源论文iclr2024 论文地址&#xff1a;https://arxiv.org/abs/2405.14616 源码地址&#xff1a; https://github.com/kwuking/TimeMixer 背景 数据是有连续性&#xff0c;周期性&#xff0c;趋势性的。我们这篇文章主要围绕的是用MLP结构来预测数据的周期性具体为&#xff…

大数据新视界 -- 大数据大厂之 Impala 性能优化:优化数据加载的实战技巧(下)(16/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

3196. I’m stuck!-13年12月CCF计算机软件能力认证

关键词 图通路&#xff0c;DFS/BFS 题目 思路 几点想说明的&#xff1a; 为什么要两个DFS&#xff1b;dfs1表示的是求从S出发能到达的所有的点&#xff1b;dfs2是考虑从T出发回溯&#xff0c;能到达的所有点&#xff0c;回溯去走&#xff0c;相当于此时T才是起点check函数&…

若依笔记(九):商城系统apk打包与调试

目录 env修改 nginx修改 apk打包 按照前文所述app版本保持2.2&#xff0c;使用uniapp作为生成工具&#xff0c;因为app版本涉及到登录、商品、支付&#xff0c;微信小程序打包需要企业主权限&#xff0c;所以使用apk打包&#xff0c;使用uniapp自带的云打包&#xff0c;本地…

算法练习记录

dp&#xff08;完全&#xff0c;线性&#xff0c;树状&#xff0c;状压&#xff0c;数位&#xff09;> 搜索 基础算法&#xff08;二分&#xff0c;前缀和&#xff0c;高精度&#xff0c;公约数&#xff0c;公倍数&#xff0c;质数&#xff0c;排序&#xff09; > 数据结…

深入浅出 ChatGPT 底层原理:Transformer

Transformer 在人工智能和自然语言处理领域,Transformer架构的出现无疑是一个里程碑式的突破。它不仅推动了GPT(Generative Pre-trained Transformer)等大型语言模型的发展,还彻底改变了我们处理和理解自然语言的方式。 GPT与Transformer GPT 近几年&#xff0c;人工智能技…

【C语言】js写一个冒泡顺序

用js写一个冒泡排序 当然可以。在JavaScript中&#xff0c;你可以使用递归的方式来实现冒泡排序算法。这是一个简单的示例&#xff1a; function bubbleSort(arr) { let len arr.length; if(len < 1){ return arr; // 如果数组只有一个元素或为空&#xff0c;直接返回 } //…

Leetcode3345. 最小可整除数位乘积 I

Every day a Leetcode 题目来源&#xff1a;3345. 最小可整除数位乘积 I 解法1&#xff1a;枚举 至多循环 10 次&#xff0c;一定会遇到个位数为 0 的数字&#xff0c;数位乘积是 0&#xff0c;一定是 t 的倍数。 所以暴力枚举即可。 代码&#xff1a; /** lc appleetcod…