当前位置: 首页 > news >正文

Android7 Input(七)App与input系统服务建立连接

概述

        本文主要讲述Android 系统创建窗口时与输入管理系统服务通过InputChannel通道建立通信桥梁的过程。

本文涉及的源码路径

frameworks/native/libs/input/InputTransport.cpp

frameworks/base/core/java/android/view/InputChannel.java

frameworks/base/core/java/android/view/ViewRootImpl.java

frameworks/base/core/jni/android_view_InputChannel.cpp

frameworks/base/core/jni/com_android_server_input_InputManagerService.cpp

frameworks/base/services/core/java/com/android/server/wm/Session.java

frameworks/base/core/java/android/view/WindowState.java

App创建InputChannel

        关于Android系统中的窗口与窗口视图相关的内容,我们有机会再进行讲述,我这里只描述创建视图时与输入相关的部分。方法是setView, 如下所示:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {......// 如果窗口可接受输入事件,创建InputChannel输入通道if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {mInputChannel = new InputChannel();}mForceDecorViewVisibility = (mWindowAttributes.privateFlags& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;try {......res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mInputChannel);} catch (RemoteException e) {......} finally {if (restore) {attrs.restore();}}......if (mInputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}......}}}

setView方法关于属于的主要逻辑如下:

1、创建一个空的InputChannel通道;

2、mWindowSession.addToDisplay中对InputChannel进行初始化;

3、注册App窗口收到InputChannel通道数据的事件回调;

InputChannel通道的真正初始化由mWindowSession.addToDisplay完成,mWindowSession是IWindowSession,属于AIDL的服务代理端,它最终调用的是IWindowSession中的服务实现端,IWinodwSession由Session实现如下所示:

final class Session extends IWindowSession.Stub {@Overridepublic int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,Rect outOutsets, InputChannel outInputChannel) {return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,outContentInsets, outStableInsets, outOutsets, outInputChannel);}
}

然后进一步调用了mService.addWindow, 而mService是WindowManagerService系统服务。因此mWindowSession.addToDisplay通过层层调用,最终调用到了WindowManagerService中的addWindow完成App和Input系统服务建立连接,如下所示:

public int addWindow(Session session, IWindow client, int seq,WindowManager.LayoutParams attrs, int viewVisibility, int displayId,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,InputChannel outInputChannel) {......WindowState win = new WindowState(this, session, client, token,attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.Slog.w(TAG_WM, "Adding window client " + client.asBinder()+ " that is dead, aborting.");return WindowManagerGlobal.ADD_APP_EXITING;}if (win.getDisplayContent() == null) {Slog.w(TAG_WM, "Adding window to Display that has been removed.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}mPolicy.adjustWindowParamsLw(win.mAttrs);win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));res = mPolicy.prepareAddWindowLw(win, attrs);if (res != WindowManagerGlobal.ADD_OKAY) {return res;}final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}return res;}

这部分的代码涉及的内容比较复杂,我们只保留与窗口输入相关的部分,从代码中可以看到,由WindowState.openInputChannel方法进一步完成InputChannel的初始化,代码如下所示:

WindowState.java

 void openInputChannel(InputChannel outInputChannel) {if (mInputChannel != null) {throw new IllegalStateException("Window already has an input channel.");}String name = makeInputChannelName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);mInputChannel = inputChannels[0];mClientChannel = inputChannels[1];mInputWindowHandle.inputChannel = inputChannels[0];if (outInputChannel != null) {// 将mClientChannel的native对象绑定到outInputChannel的native对象mClientChannel.transferTo(outInputChannel);// 删除mClientChannel自己的native对象mClientChannel.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);}mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);}

该方法的主要逻辑如下:

1、App 创建Inputchannel通道, 用自己Client的InutChannel[0],初始化outputChannel通道;

2、将InutChannel[1] 注册到input系统服务中的InputDispatcher中;

注册服务端InputChannel到InputDispatcher

        上一章节,我们讲述了App创建输入通道的过程,本章节我们讲述App如何与input系统服务建立通信的桥梁的过程。建立的过程,我们可以把它抽象成linux中pipe管道,一端为App所持有outChannel,另外一端inputChannels[1]通过,mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle)的注册,让input系统服务持有。主要实现是通过调用InputManagerService中的registerInputChannel,如下所示:

 public void registerInputChannel(InputChannel inputChannel,InputWindowHandle inputWindowHandle) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null.");}nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);}

该方法进一步直接调用nativeRegisterInputChannel JNI方法如下所示:

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);......sp<InputWindowHandle> inputWindowHandle =android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);status_t status = im->registerInputChannel(env, inputChannel, inputWindowHandle, monitor);if (status) {String8 message;message.appendFormat("Failed to register input channel.  status=%d", status);jniThrowRuntimeException(env, message.string());return;}......
}

然后继续追踪,最终调用到了我们熟悉的InputDispatcher中的registerInputChannel,如下所示:

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {......    { // acquire lockAutoMutex _l(mLock);sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);int fd = inputChannel->getFd();mConnectionsByFd.add(fd, connection);mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);} // release lock// Wake the looper because some connections have changed.mLooper->wake();return OK;
}

该方法的实现逻辑如下:

1、为每一个请求注册Inputchannel创建一个Connection进行管理,并将待注册的InputChannel的通道描述符和Connection建立关联容器映射;

2、将注册的InputChannel通道加入到InputDispatcher中Looper事件监控,当App向客户端的InputChannel写入信息时,则触发InputDispatcher中的handleReceiveCallback回调方法的调用,关于App和Input系统服务的通信过程,我们后续的章节进行讲述。

App注册事件回调

上一个章节,我们讲述了App和input系统服务建立通信连接的过程,那InputDispatcher发送msg时,App如何接收呢,核心实现如下所示:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {.......if (mInputChannel != null) {if (mInputQueueCallback != null) {mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}
}

当InputDispacther向InputChannel写入事件信息时,App最终通过WindowInputEventReceiver进行事件的处理,App接受input事件和处理流程我们在下一篇进行讲述。

总结

        本文主要讲述了App初始化创建窗口时的与输入管理相关的处理流程,App为了能接收到系统上报的输入事件,通过InputChannel作为桥梁,将App和Input系统服务建立了通信桥梁,实现的核心原理就是创建socket对,并通过事件监听的方式,从socket中获取监听的信息,并进行处理,当App处理完信息也则通过socket对,告诉input系统服务的处理结果。后续章节我们会讲述这个处理流程。

相关文章:

  • 灵光一现的问题和常见错误1
  • 搭建基于Windows平台的http文件服务(miniserve+filebrowser+nssm)
  • Datawhale PyPOTS时间序列5月第3次笔记
  • 湖北理元理律师事务所:债务优化中的双维支持实践解析
  • 一分钟用 MCP 上线一个 贪吃蛇 小游戏(CodeBuddy版)
  • java中的运算符
  • 多线程(4)——线程安全,锁
  • 数学复习笔记 16
  • 在 Linux 上安装 MATLAB:完整指南与疑难解决方案
  • 交流学习 | 江西同为科技有限公司赴海尔总部考察交流
  • Spring源码之解决循环依赖 三级缓存
  • Python二进制运算:高效操作与实用技巧
  • OpenHarmony外设驱动使用 (二),Camera
  • [ctfshow web入门] web118
  • hysAnalyser 从MPEG-TS导出ES功能说明
  • Leaflet使用SVG创建动态Legend
  • wifiactivity 界面 要在哪里注册,注销广播?onResume() vs onPause() 的本质区别
  • 安装nerdctl和buildkitd脚本命令
  • LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作
  • stack和queue简单模拟实现
  • 家国万里·时光故事会|构筑中国船舰钢筋铁骨,她在焊花里展现工匠风范
  • 从良渚到三星堆:一江水串起了5000年的文明对话
  • 看展 | 黄永玉新作展,感受赤子般的生命力
  • 全国多家健身房女性月卡延长,补足因月经期耽误的健身时间
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 爬坡难下坡险,居民出行难题如何解?