由于项目需要,需要了解下android touch数据的传递流程,看了下代码后把过程记录下来,方便后面查阅。
本文章基于Android11开源源码,所有代码均可以在aosp官方提供的地址去查阅下载,Android11具体流程可能和其他Android版本有点不一致,
如果文中有不对的地方欢迎同学们指出一起讨论交流~
从4条线(4个方向)来分析android 的touch 数据传送机制,沿途主要关注touch数据从kernel出来到app的路线,把这条路打通,不关注细节,以及touch数据是如何决定给到哪个activity(窗口,view)的,
目录
一句话概括,app往wms那边添加窗口的同时,wms创建一对socket pair,用InputChannel封装,一个给app,一个给InputFlinger,之后InputFlinger通过这个unix socket fd把touch数据发给app。
拿button举例子,整体流程大概是,app起来后,会在ViewRootImpl 那边往wms添加window的时候,同时创建一个InputChannel 也一起带过去,wms会作为中介把这个InputChannel 传给InputFlinger, 点击button后,inputflinger读到从kernel出来的数据后,会通过这个InputChannel把数据给到ViewRootImpl, 接着通过ViewPostImeInputStage给到View,再创建一个PerformClick通过View.post()去执行,回调button的onClick()接口。(怎么知道给到哪个activity哪个window哪个View就是接下来分析过程中理清的)
ViewRootImpl 注册 InputChannel
app activity 起来后,创建了PhoneWindow 后往wms 添加window时,在ViewRootImpl创建了InputChannel作为出参一起传过去,wms通过socket pair 创建了2个InputChannel,一个作为client 一个作为server,client把fd给ViewRootImpl传过来的那个InputChannel, server给 传给InputFlinger, InputFlinger 创建Connection 保存下来,之后用它来回传touch数据给app。
看下图:
看下代码:
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
//......
//创建 InputChannel
inputChannel = new InputChannel();
//在添加窗口的时候通过binder ipc一起给到wms
//note1
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
//同时创建WindowInputEventReceiver用来接收touch数据(其实不只touch,
//应该是包括按键,键盘啥的,我们目前先就只关注touch)
//note2
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
//......
//创建用于处理input数据的InputStage,
//note19
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
//frameworks/base/core/java/android/view/IWindowSession.aidl
int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
//注意这边inputChannel是作为出参传进去的,后面在WindowState那边创建了socket pair后
//会重新给他赋值使其作为接收数据的客户端拥有可以接收touch数据的fd。
//当然这边是跨进程的然后中间是有binder做了处理简化了流程。
跟下mWindowSession.addToDisplayAsUser(),
其中这边mWindowSession 实例为 Session对象, 是在WindowManagerGlobal 那边从wms拿到的,之后在创建ViewRootImpl的时候再传过来,可以简单看下~
//在这边赋值的
//frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
//.......
mWindowSession = session;
//.......
}
//这边传过来的
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//......
//创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
//.......
}
//这边通过binder ipc从wms那边去获取的,再接着看下wms实现
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
//......
//从wms那边拿
sWindowSession = windowManager.openSession(......);
return sWindowSession;
}
//wms直接new了一个Session对象返回
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
好了,确认mWindowSession 就是Session了,接着回到note1的 mWindowSession.addToDisplayAsUser(),继续看InputChanne的注册,其实就是通过binder ipc到了wms那边
//frameworks/base/services/core/java/com/android/server/wm/Session.java
public 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) {
//......
//给WindowState了
win.openInputChannel(outInputChannel);
//......
}
//接着往WindowState看
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) {
//......
//创建一对InputChannle
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//作为server和client
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
//server端InputFlinger那边
//这边wms、InputManagerService、InputFlinger 共进程,直接调用后,
//InputManagerService也是直接调用给到InputFlinger
mWmService.mInputManager.registerInputChannel(mInputChannel);
//......
//client 赋值给出参然后返回到ViewRootImpl作为接收touch数据客户端
mClientChannel.transferTo(outInputChannel);
//......
}
其实也就是在这创建了2个能进行通信的c++ InputChannel,分别作为通信的client和server,里边分别包含socket pair 创建的2个fd,做为client的InputChannel赋值给ViewRootClient 传过来的java InputChannel,做为server的InputChannel 通过进程内函数(非binder)调用给到InputFlinger那边,分别用来接收和发送touch数据。
接着看 mWmService.mInputManager.registerInputChannel(),
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel);
}
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobject inputChannelObj) {
//......
status_t status = im->registerInputChannel(env, inputChannel);
//......
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
//到InputDispatcher那边了
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif
{ // acquire lock
std::scoped_lock _l(mLock);
//查重
sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
if (existingConnection != nullptr) {
ALOGW("Attempted to regis