Android 11 输入系统之InputDispatcher和应用窗口建立联系

InputDispatcher把输入事件传给应用之前,需要和应用窗口建立联系,了解了这个过程,就清楚了APP进程和InputDispatcher线程也就是SystemServer进程之间是如何传输数据了
我们向窗口addView的时候,都会调用到ViewRootImpl的setView方法,从这个方法开始分析(只关注和input有关的流程)

//frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {if (mView == null) {//省略InputChannel inputChannel = null;if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel();//1}try {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();adjustLayoutParamsForCompatibility(mWindowAttributes);res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mDisplayCutout, inputChannel,mTempInsets, mTempControls);//2setFrame(mTmpFrame);//省略if (inputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());//3}//省略}	}
}

注释1处新建一个InputChannel 对象。注释2处是一个远程调用,也就是服务端的addToDisplayAsUser方法,注意inputChannel参数在aidl文件中标记的是out,说明inputChannel是根据远端返回的数据初始化的。注释3处创建WindowInputEventReceiver对象。
先来看看addToDisplayAsUser方法

//frameworks\base\services\core\java\com\android\server\wm\Session.java@Overridepublic int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, Rect outFrame,Rect outContentInsets, Rect outStableInsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,outInsetsState, outActiveControls, userId);}

直接调用WMS的addWindow方法

//frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,int requestUserId) {//省略final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);//省略final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}//省略

调用WindowState的openInputChannel方法

//frameworks\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {if (mInputChannel != null) {throw new IllegalStateException("Window already has an input channel.");}String name = getName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1mInputChannel = inputChannels[0];mClientChannel = inputChannels[1];mWmService.mInputManager.registerInputChannel(mInputChannel);//2mInputWindowHandle.token = mInputChannel.getToken();if (outInputChannel != null) {mClientChannel.transferTo(outInputChannel);//3mClientChannel.dispose();mClientChannel = null;} else {// If the window died visible, we setup a dummy input channel, so that taps// can still detected by input monitor channel, and we can relaunch the app.// Create dummy event receiver that simply reports all events as handled.mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);}mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);}

该方法主要完成以下工作:

  1. 创建socketpair,得到两个文件句柄,分别封装在InputChannel对象中
  2. 因为wms和inputDispatcher都是在SystemServer进程中,所以其中一个InputChannel即mInputChannel 只要直接注册就行了,不需要跨进程通信
  3. 将mClientChannel复制给outInputChannel,用于回传给APP应用进程

socketpair的创建过程

//frameworks\base\core\java\android\view\InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {//省略return nativeOpenInputChannelPair(name);
}

java层的InputChannel只是一个壳,直接发起JNI调用

//frameworks\base\core\jni\android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,jclass clazz, jstring nameObj) {ScopedUtfChars nameChars(env, nameObj);std::string name = nameChars.c_str();sp<InputChannel> serverChannel;sp<InputChannel> clientChannel;status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);//1jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);env->SetObjectArrayElement(channelPair, 0, serverChannelObj);//放入元素env->SetObjectArrayElement(channelPair, 1, clientChannelObj);//放入元素return channelPair;
}

注释1处创建socketpair,并创建两个C++层的InputChannel对象

//frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {//创建socketpairstatus_t result = -errno;ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",name.c_str(), errno);outServerChannel.clear();outClientChannel.clear();return result;}/*设置buffer的大小为32k*/int bufferSize = SOCKET_BUFFER_SIZE;setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));sp<IBinder> token = new BBinder();//创建tokenstd::string serverChannelName = name + " (server)";android::base::unique_fd serverFd(sockets[0]);outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);//创建服务端InputChannel对象std::string clientChannelName = name + " (client)";android::base::unique_fd clientFd(sockets[1]);outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);//创建客户端InputChannel对象return OK;
}

可以看出,通过socketpair的两个fd,分别构造了Native层的InputChannel对象,同时,这个token也很重要,用户窗口中InputWindowInfo的token和这个是一致的
两个InputChannel构造完成后,其中一个需要通过binder回传给APP客户端(实际上就是写fd,然后客户端根据fd重新创建InputChannel),接下来分析服务端以及客户端的处理

注册InputChannel到InputDispatcher中

回到openInputChannel方法,InputChannel构造完成后,调用registerInputChannel,将服务端的InputChannel注册到InputDispatcher中

public void registerInputChannel(InputChannel inputChannel) {//省略nativeRegisterInputChannel(mPtr, inputChannel);
}

也是直接调用JNI方法

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);//取出InputChannelstatus_t status = im->registerInputChannel(env, inputChannel);//1//省略
}

注释1处,调用NativeInputManager的registerInputChannel方法

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,const sp<InputChannel>& inputChannel) {ATRACE_CALL();return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}

继续调用InputDispatcher的registerInputChannel方法

//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {{ // acquire lock//省略sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);int fd = inputChannel->getFd();mConnectionsByFd[fd] = connection;mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);} // release lock// Wake the looper because some connections have changed.mLooper->wake();return OK;
}

首先根据inputChannel创建了一个Connection对象,然后取出inputChannel的fd,将该connection放入mConnectionsByFd数组,注意,数组的下标是fd,可以根据fd找到这个connection。同时把inputChannel放入mInputChannelsByToken数组
最后将该fd加到Looper中,Looper也是使用的epoll机制,当客户端写入事件时(主要是告知输入事件处理完毕),这个fd就表明有事件读入,就会调用handleReceiveCallback函数

客户端处理InputChannel

回到setView方法,客户端接收到服务端返回的InputChannel后,创建WindowInputEventReceiver对象,WindowInputEventReceiver继承自InputEventReceiver

//frameworks\base\core\java\android\view\InputEventReceiver.javapublic InputEventReceiver(InputChannel inputChannel, Looper looper) {//省略mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);//1mCloseGuard.open("dispose");}

注释1处调用nativeInit方法

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);//取出//省略sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);//创建NativeInputEventReceiver对象status_t status = receiver->initialize();//1//省略receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}

注释1处调用NativeInputEventReceiver的initialize方法,在initialize方法中直接调用setFdEvents,将客户端inputChannel 中的fd加到Looper中

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {mFdEvents = events;int fd = mInputConsumer.getChannel()->getFd();if (events) {mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);//注意第4个参数为当前的NativeInputEventReceiver对象} else {mMessageQueue->getLooper()->removeFd(fd);}}
}

添加到Looper后,如果后续有事件到来,即InputDispatcher发送过来了输入事件,则会调用NativeInputEventReceiver自己的handleEvent方法

总结

可以看出InputDispatcher和客户端进程之间通讯是采用socket的方式,而因为这里已经明确是是它们两个之间通讯,所以这里使用了更加方便的socketpair,socketpair得到两个fd分别给InputDispatcher和客户端进程,其中使用binder将其中的一个fd回传给客户端。

InputDispatcher由于和wms是在同一个进程,所以可以直接使用这个fd。InputDispatcher会创建Connection对象,并维护两个数组。并将fd添加到Looper中。客户端拿到这个fd也同样是加到Looper中。

在这里插入图片描述

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

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

相关文章

Docker 部署 Nginx 实现一个极简的 负载均衡

背景: Nginx是异步框架的网页服务器&#xff0c;其常用作反向代理(负载均衡器)。在一般的小项目中, 服务器不多, 如果不考虑使用服务注册与发现, 使用Nginx 可以容易实现负载均衡。 在特此写一个快速入门 Nginx 的技术贴, 使用 Docker 部署 Nginx, 实现一个极简的加权轮询负载均…

现在的原创内容博客 SEO 最好就选谷歌和必应!

当我们在国内讨论搜索引擎优化的时候&#xff0c;我们经常讨论的是百度 SEO&#xff0c;很少提及 Bing 搜索与 Google 搜索&#xff0c;但随着跨境电商的崛起&#xff0c;在国内做外贸 SEO 的小伙伴越来越多&#xff0c;有效的了解 Bing 搜索与 Google 搜索的优化规则是很有必要…

开源aodh学习小结

1 介绍 aodh是openstack监控服务&#xff08;Telemetry&#xff09;下的一个模块&#xff0c;telemetry下还有一个模块ceilometer OpenStack Docs: 2024.1 Administrator Guides Get Started on the Open Source Cloud Platform - OpenStack Telemetry - OpenStack 1.1 代码仓…

softmax函数与交叉熵损失详解

文章目录 一、softmax函数1.1 引入指数形式的优点1.2 引入指数形式的缺点 二、交叉熵损失函数2.1 交叉熵损失函数2.2 softmax与交叉熵损失 参考资料 一、softmax函数 softmax用于多分类过程中&#xff0c;它将多个神经元的输出&#xff0c;映射到&#xff08;0,1&#xff09;区…

【C++ 内存管理】深拷贝和浅拷贝你了解吗?

文章目录 1.深拷贝2.浅拷贝3.深拷贝和浅拷贝 1.深拷贝 &#x1f34e; 深拷⻉: 是对对象的完全独⽴复制&#xff0c;包括对象内部动态分配的资源。在深拷⻉中&#xff0c;不仅复制对象的值&#xff0c;还会复制对象所指向的堆上的数据。 特点&#xff1a; &#x1f427;① 复制对…

记录一下 log4j的漏洞

目录 背景 bug的产生 bug复现 JNDI 网络安全学习路线 &#xff08;2024最新整理&#xff09; 学习资料的推荐 1.视频教程 2.SRC技术文档&PDF书籍 3.大厂面试题 特别声明&#xff1a; 背景 log4j这次的bug&#xff0c;我相信大家都已经知道了&#xff0c;仅以…

【unity小技巧】减少Unity中的构建打包大小

文章目录 正常默认打包查看编辑器打包日志压缩图片压缩网格模型压缩贴图压缩音频文件只打64位包最终大小完结 正常默认打包 这里以安卓为例。先什么都不干&#xff0c;直接打包安卓apk&#xff0c;查看包大小 查看编辑器打包日志 搜索build report构建报告。构建报告我们应该…

Pytorch学习-引言

Pytorch相关链接 Pytorch官方网站 https://pytorch.org/ Pytorch的Github仓库 https://github.com/pytorch/pytorch Pytorch论坛 https://discuss.pytorch.org/ Pytorch离线下载包链接 https://download.pytorch.org/whl/torch_stable.html Pytorch学习视频推荐链接 http://【…

手写一个SPI FLASH 读写擦除控制器

文章目录 flash读写数据的特点1. 扇擦除SE&#xff08;Sector Erase&#xff09;1.1 flash_se 模块设计1.1.1 信号连接示意图&#xff1a;1.1.2 SE状态机1.1.3 波形图设计&#xff1a;1.1.4 代码 2. 页写PP(Page Program)2.1 flash_pp模块设计2.1.1 信号连接示意图&#xff1a;…

JavaScript 对象入门:基础用法全解析

目录 对象 语法 属性和访问 方法和调用 this关键字 null 遍历对象 内置对象 Math 属性 方法 Date 创建日期对象 获取和设置日期 ⭐对象 对象是 JavaScript 数据类型的一种&#xff0c;数据类型也包括数值类型、字符串类型、布尔类型、undefined。对象数据类型可…

程序员之路:裁员与内卷下的生存之道

作为一名普通的程序员&#xff0c;身处这个瞬息万变的IT行业&#xff0c;面对着今年不断加剧的裁员浪潮和日益激烈的内卷竞争&#xff0c;我时常感到焦虑和不安。然而&#xff0c;正是这些挑战&#xff0c;让我们更加深入地思考了在这个行业中&#xff0c;我们该如何找到自己的…

2024统计建模中国新质生产力统计测度与时空演变及其驱动因素研究

高质量成品论文46页word版本1.5w字书写完整数据集1000行py代码一等奖论文&#xff01;这里仅展示部分内容&#xff0c;完整版在下面的链接。 【1.5w字全网最佳】2024统计建模大赛高质量成品论文39页配套完整代码运行全套数据集https://www.jdmm.cc/file/2710661/ 中国新质生产…

【2024HNCTF】密码组部分出题记录

2024H&NCTF 密码组部分出题记录 题目&#xff1a;BabyPQ、HappyDance 文章目录 2024H&NCTF 密码组部分出题记录BabyPQ | 签到HappyDance BabyPQ | 签到 本题为nc交互题&#xff0c;之所以采用这种形式&#xff0c;是因为可能有很多密码新师傅们不了解这种赛题形式&a…

AI机器人火了,探讨早就可以帮我们开拓市场的中关村科金语音机器人

近期AI机器人给我们带来了不少惊喜&#xff0c;比如国外 Figuer 联合 OpenAI 做了 Chatgpt 机器人&#xff0c;可以通过对话后推理干活&#xff0c;国内仿生机器人员工也开始量产&#xff0c;看到AI机器人不禁想到会不会替代我们的工作&#xff1f;我们了解到很多机器人厂家的…

#初阶模板

目录 1.泛型编程 2.模板 &#xff08;1&#xff09;函数模板 &#xff08;2&#xff09;举个栗子&#xff08;模板的使用&#xff09; 1.模板的声明有两种形式 2.函数模板的原理 &#xff08;3&#xff09;模板参数的匹配原则 &#xff08;4&#xff09;类模板 类模板…

AI图像生成-原理

一、图像生成流程总结 【AI绘画】深入理解Stable Diffusion&#xff01;站内首个深入教程&#xff0c;30分钟从原理到模型训练 买不到的课程_哔哩哔哩_bilibili 二、如果只是用comfy UI生成图片 1、找到下面几个文件&#xff0c;把对应模型移动到对应文件夹即可使用 2、选择对…

大学c语言基础很差,能不能学51单片机?会不会很困难?

开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些51单片机学习方法和资料&#xff0c;让我不断提升自己&#xff0c;感谢帮助过我的人&#xff0c; 如大家和我一样…

python turtle 升国旗

​一、导语 大家好,前段时间,我们画出了五星红旗,今天我们要用Python的Turtle库来绘制一个五星红旗,并让国旗上升,让我们一起来感受编程与艺术的完美结合吧!领略国家的强大!爱祖国,做一个遵纪守法的好公民。 二、效果展示 升国旗 三、开发过程 一、准备工作 首先我们…

OpenAI深夜震撼发布最新模型GPT-4o,送上最快速便捷教程

北京时间5月14日凌晨&#xff0c;有人说OpenAI一夜改变了历史。 在我们的深夜、太平洋时间的上午 10 点&#xff0c;OpenAI 召开春季发布会&#xff0c;公布了最新的GPT-4o模型&#xff0c;o代表Omnimodel&#xff08;全能模型&#xff09;。20多分钟的演示直播&#xff0c;展…

美国加州正测试ChatGPT等生成式AI,在4大部门应用

5月11日&#xff0c;美联社消息&#xff0c;美国加州政府正在测试ChatGPT等生成式AI&#xff0c;应用在税收和收费管理部、交通部、公共卫生部以及卫生与公众服务部4大部门。 测试时间6个月&#xff0c;为其提供技术支持的一共有5家公司&#xff0c;分别是OpenAI、Anthropic、…