Android11 InputManagerService启动流程分析
InputManagerService在systemserver进程中被启动
//frameworks\base\services\java\com\android\server\SystemServer.java
t.traceBegin("StartInputManagerService");
inputManager = new InputManagerService(context);//1
t.traceEnd();
//省略
//注册服务
ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);//2
//省略
inputManager.start();//3
注释1处创建InputManagerService对象,注释2处注册input服务。注释3处调用inputManager的start方法。先来看一下InputManagerService对象的创建过程
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
public InputManagerService(Context context) {//省略mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//省略
}
继续调用nativeInit方法,这是一个native方法,会通过JNI调用到com_android_server_input_InputManagerService.cpp的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},//省略
注释1处,继续创建NativeInputManager对象
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :mLooper(looper), mInteractive(true) {JNIEnv* env = jniEnv();mServiceObj = env->NewGlobalRef(serviceObj);//省略mInteractive = true;mInputManager = new InputManager(this, this);//1defaultServiceManager()->addService(String16("inputflinger"),mInputManager, false);//2
}
注释1处创建InputManager对象,注释2处,这里又在native层注册了一个inputflinger服务。
//frameworks\native\services\inputflinger\InputManager.cpp
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {mDispatcher = createInputDispatcher(dispatcherPolicy);//1mClassifier = new InputClassifier(mDispatcher);//2mReader = createInputReader(readerPolicy, mClassifier);//3
}
注释1处创建InputDispatcher对象,注释2处创建InputClassifier对象,注释3处创建InputReader对象。接下来看一下各个对象的创建过程
InputDispatcher:
//frameworks\native\services\inputflinger\dispatcher\InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {return new android::inputdispatcher::InputDispatcher(policy);
}//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy): mPolicy(policy),//1mPendingEvent(nullptr),mLastDropReason(DropReason::NOT_DROPPED),mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),mAppSwitchSawKeyDown(false),mAppSwitchDueTime(LONG_LONG_MAX),mNextUnblockedEvent(nullptr),mDispatchEnabled(false),mDispatchFrozen(false),mInputFilterEnabled(false),// mInTouchMode will be initialized by the WindowManager to the default device config.// To avoid leaking stack in case that call never comes, and for tests,// initialize it here anyways.mInTouchMode(true),mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {mLooper = new Looper(false);mReporter = createInputReporter();mKeyRepeatState.lastKeyEntry = nullptr;policy->getDispatcherConfiguration(&mConfig);
}
注释1处将policy赋给mPolicy,这里的policy就是传进来的NativeInputManager对象。所以,InputDispatcher中的mPolicy指向的是NativeInputManager对象
InputClassifier:
//frameworks\native\services\inputflinger\InputClassifier.cpp
InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener): mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
mListener指向listener,这里的listener就是我们前面创建的InputDispatcher对象,所以,InputClassifier中的mListener指向的是一个InputDispatcher对象
InputReader:
//frameworks\native\services\inputflinger\reader\InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {return new InputReader(std::make_unique<EventHub>(), policy, listener);//1
}
注释1处先创建一个EventHub,然后创建一个InputReader对象
//frameworks\native\services\inputflinger\reader\EventHub.cpp
EventHub::EventHub(void): mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),mNextDeviceId(1),mControllerNumbers(),mOpeningDevices(nullptr),mClosingDevices(nullptr),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {ensureProcessCanBlockSuspend();mEpollFd = epoll_create1(EPOLL_CLOEXEC);//1LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));mINotifyFd = inotify_init();//2mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);//3LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,strerror(errno));//省略struct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP;eventItem.data.fd = mINotifyFd;int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);//4LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);//省略
注释1处创建一个epoll,用于监听是否有数据可供读入。注释2处初始化inotify,并在注释3处将DEVICE_PATH添加到inotify的监测中,DEVICE_PATH为/dev/input。注释4处将这个inotify又添加到epoll的监测中。如果DEVICE_PATH目录下有设备节点增加或删除,inotify就可以监测到,mINotifyFd就有数据可供读入,epoll也就可以监测到了。
接着回到InputReader的创建里面
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener): mContext(this),mEventHub(eventHub),//1mPolicy(policy),//2mGlobalMetaState(0),mGeneration(1),mNextInputDeviceId(END_RESERVED_ID),mDisableVirtualKeysTimeout(LLONG_MIN),mNextTimeout(LLONG_MAX),mConfigurationChangesToRefresh(0) {mQueuedListener = new QueuedInputListener(listener);//3{ // acquire lockAutoMutex _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}
注释1处将mEventHub指向上面创建的EventHub对象。注释2处将mPolicy指向传进来的policy,而传进来的policy为NativeInputManager对象,所以,InputReader中的mPolicy也是指向NativeInputManager对象,注释3处根据传进来的InputClassifier对象构造QueuedInputListener
//frameworks\native\services\inputflinger\InputListener.cpp
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :mInnerListener(innerListener) {
}
所以QueuedInputListener对象中的mInnerListener指向的是一个InputClassifier对象
至此,各个对象的创建都已完毕,来看一下各对象简易的类图

接着回到systemserver中,InputManagerService对象创建后,就会调用该对象的start方法
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
public void start() {Slog.i(TAG, "Starting input manager");nativeStart(mPtr);
//省略
继续调用nativeStart,这也是一个Native方法,调用com_android_server_input_InputManagerService.cpp的nativeStart方法
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);status_t result = im->getInputManager()->start();//2if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}static const JNINativeMethod gInputManagerMethods[] = {/* name, signature, funcPtr *///省略{"nativeStart", "(J)V", (void*)nativeStart},//省略
注释1处getInputManager返回的是上面创建的InputManager对象,调用InputManager的start方法
//frameworks\native\services\inputflinger\InputManager.cpp
status_t InputManager::start() {status_t result = mDispatcher->start();//1if (result) {ALOGE("Could not start InputDispatcher thread due to error %d.", result);return result;}result = mReader->start();//2if (result) {ALOGE("Could not start InputReader due to error %d.", result);mDispatcher->stop();return result;}return OK;
}
注释1处调用InputDispatcher的start方法,注释2处调用InputReader的start方法
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}//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;
}
可以看出,分别启动了InputDispatcher和InputReader线程。InputReader线程启动调用loopOnce读取事件,InputDispatcher线程启动调用dispatchOnce分发事件
总结
在InputManagerService启动过程中,会启动两个线程,InputReader和InputDispatcher线程,用于读取数据和分发数据。其中在InputReader对象的创建过程中,会创建EventHub对象,初始化Epoll和inotify用于监测/dev/input目录
InputDispatcher和InputReader对象的创建流程

InputDispatcher和InputReader线程的启动流程:

相关文章:
Android11 InputManagerService启动流程分析
InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…...
【计算机网络篇】数据链路层(8)共享式以太网的退避算法和信道利用率
文章目录 🛸共享式以太网的退避算法🥚截断二进制指数算法 🍔共享式以太网的信道利用率 🛸共享式以太网的退避算法 在使用CSMA/CD协议的共享总线以太网中,正在发送帧的站点一边发送帧一边检测碰撞,当检测到…...
wordpress主题 7B2 PRO主题5.4.2免授权直接安装
内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 WordPress 资讯、资源、社交、商城、圈子、导航等多功能商用主题:B2 PRO 其设计风格专业且时尚,功能十分强大,包括多栏布局、自定义页面、强大的主…...
Dubbo基本使用
Dubbo基本使用 1.项目介绍2.开发步骤2.1 启动注册中心2.2 初始化项目2.3 添加 Maven 依赖2.3.1 父pom.xml2.3.1 consumer模块和provider模块pom.xml 2.4 定义服务接口2.5 定义服务端的实现2.6 配置服务端 Yaml 配置文件2.7 配置消费端 Yaml 配置文件2.8 基于 Spring 配置服务端…...
JS解密之新js加密实战(二)
前言 上次发了一篇关于新加密的,只解了前边两层,这中间家里各种事情因素影响,没有继续进一步研究,今天百忙之中抽空发布第二篇,关于其中的一小段加密片段,我认为分割成多个小片段是更容易被理解的。逻辑相…...
tsconfig 备忘清单
前言 ❝ Nealyang/blog0 使用 ts 已多年,但是貌似对于 tsconfig 总是记忆不清,每次都是 cv 历史项目,所以写了这篇备忘录,希望能帮助到大家。 本文总结整理自 Matt Pocock 的一篇文章3,加以个人理解,并做了…...
jmeter后置处理器提取到的参数因为换行符导致json解析错误
现象: {"message":"JSON parse error: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Ill…...
栈与队列的实现
前言 本次博客将要实现一下栈和队列,好吧 他们两个既可以使用动态数组也可以使用链表来实现 本次会有详细的讲解 栈的实现 栈的基础知识 什么是栈呢? 栈的性质是后进先出 来画个图来理解 当然可不可以出一个进一个呢,当然可以了 比如…...
线性集合:ArrayList,LinkedList,Vector/Stack
共同点:都是线性集合 ArrayList ArrayList 底层是基于数组实现的,并且实现了动态扩容(当需要添加新元素时,如果 elementData 数组已满,则会自动扩容,新的容量将是原来的 1.5 倍),来…...
llama3 发布!大语言模型新选择 | 开源日报 No.251
meta-llama/llama Stars: 53.0k License: NOASSERTION llama 是用于 Llama 模型推理的代码。 提供了预训练和微调的 Llama 语言模型,参数范围从 7B 到 70B。可以通过下载脚本获取模型权重和 tokenizer。支持在本地快速运行推理,并提供不同规格的模型并…...
SpringBoot 具体是做什么的?
Spring Boot是一个用于构建独立的、生产级别的、基于Spring框架的应用程序的开源框架。它的目标是简化Spring应用程序的开发和部署过程,通过提供一种快速、便捷的方式来创建Spring应用程序,同时保持Spring的灵活性和强大特性。 1. 简化Spring应用程序开…...
Debian常用命令
Debian是一个开源的Unix-like操作系统,提供了大量的软件包供用户安装和使用。在Debian系统中,命令行界面(CLI)是用户与系统进行交互的重要工具。以下是Debian中一些常用的命令及其详细解释: 文件和目录操作命令&#x…...
常见的前端框架
常用的前端框架有以下几种: 模型 React:由Facebook开发的一款前端框架,采用虚拟DOM的概念,可高效地更新页面。Vue.js:一款轻量级的前端框架,易学易用,支持组件化开发和双向数据绑定。AngularJ…...
初学者如何选择ARM开发硬件?
在开始前我有一些资料,是我根据网友给的问题精心整理了一份「ARM的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!如果你没有ARM开发经验࿰…...
Mysql 多表查询,内外连接
内连接: 隐式内连接 使用sql语句直接进行多表查询 select 字段列表 from 表1 , 表2 where 条件 … ; 显式内连接 将‘,’改为 inner join 连接两个表的 on select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 … ; select emp.id, emp.name, …...
【C语言】函数
目录 一、函数的概念 二、库函数 2.1 ❥ 标准库 2.2 ❥ 库函数的使用方法 三、自定义函数 四、形参和实参 4.1 ❥ 实参(实际参数) 4.2 ❥ 形参(形式参数) 五、return语句 六、函数的调用 6.1 ❥ 传值调用 6.2 ❥ 传址调…...
【LeetCode】每日一题 2024_5_13 腐烂的橘子(经典多源 BFS)
文章目录 LeetCode?启动!!!题目:找出不同元素数目差数组题目描述代码与解题思路 每天进步一点点 LeetCode?启动!!! 好久没写每日一题题解了,今天重新起航 干…...
【Linux系统编程】第十七弹---进程理解
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、进程的基本概念 2、描述进程-PCB 2.1、什么是PCB 2.2、为什么要有PCB 3、task_ struct 3.1、启动进程 3.2、创建进程…...
【网络安全入门】你必须要有的学习工具(附安装包)零基础入门到进阶,看这一篇就够了!
工欲善其事必先利其器 在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧! Web安全类 Web类工具主要是通过各种扫描工具,发现web站点存在的各种漏洞如sql注入、xss等。从而获取系统权限,常用的…...
【解决】:git clone项目报错fatal: fetch-pack: invalid index-pack output
象:之前一直使用gitee将个人学习和工作相关记录上传到个人gitee仓库,一直没出现过问题。直到有一天换电脑重新拉取代码发现出了问题,具体如下图: 原因分析: 经过查询发现主要原因是因为git clone的远程仓库的项目过大…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...
python可视化:俄乌战争时间线关键节点与深层原因
俄乌战争时间线可视化分析:关键节点与深层原因 俄乌战争是21世纪欧洲最具影响力的地缘政治冲突之一,自2022年2月爆发以来已持续超过3年。 本文将通过Python可视化工具,系统分析这场战争的时间线、关键节点及其背后的深层原因,全面…...
Electron简介(附电子书学习资料)
一、什么是Electron? Electron 是一个由 GitHub 开发的 开源框架,允许开发者使用 Web技术(HTML、CSS、JavaScript) 构建跨平台的桌面应用程序(Windows、macOS、Linux)。它将 Chromium浏览器内核 和 Node.j…...
