原创内容,转载请注明出处,多谢配合。
上节讲到InputDispatcher通过publishKeyEvent把input事件发送给客户端,我们知道InputDispatcher是属于system_server进程,而客户端属于应用进程,两种通信属于跨进程通信,那么本篇文章就来分析下system_server与应用建立通信的过程。
源头得追溯到应用的setView开始:
frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel(); //创建客户端的InputChannel对象
}
//通过Binder调用,进入system进程的Session
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);//这里将客户端的InputChannel对象作为参数传入
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//创建WindowInputEventReceiver对象
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
}
应用在setView()的是会调用IWindowSession的addToDisplay()函数。
前面图形系统系列提过这个函数,这是添加窗口流程,它包含了App与SurfaceFlinger服务建立连接的部分,也是包含了今天要介绍的InputChannel建立连接的部分。它本身是个binder调用,服务端是WMS,最终调用的是WMS的addWindow方法。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
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是窗口对象
WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
...
if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
// 创建socket pair用于通信
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// 将服务端的socket赋给服务端的WindowState
win.setInputChannel(inputChannels[0]);
// 将客户端的socket放入outInputChannel,最终返回客户端应用进程
inputChannels[1].transferTo(outInputChannel);
// 将服务端的socket(win.mInputChannel)注册到InputDispatcher中
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
InputChannel通过InputChannel.openInputChannelPair分别窗建一对InputChannel,然后将Server端的InputChannel注册到InputDispatcher中,将Client端的InputChannel返回给客户端应用。
socket pair的创建过程这里不详细说,简单总结下:
openInputChannelPair 生成了两个Socket的fd, 代表一个双向通道的两端。初始化了两端的包括Native层和Java层的InputChannel对象,InputChannel封装了name和fd。
1)Server端的InputChannel注册到InputDispatcher中:
InputManagerService.java 执行registerInputChannel 走的JNI:nativeRegisterInputChannel,最终调到如下方法:
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */, const sp& inputChannel, const sp& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle, monitor);
}
通过InputDispatcher的registerInputChannel注册
frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp& inputChannel, const sp& inputWindowHandle, bool monitor) {
sp connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
...
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
mLooper->wake();
return OK;
}
frameworks/native/services/inputflinger/InputDispatcher.h
class Connection : public RefBase {
...
sp inputChannel; // never null
...
Queue outboundQueue;
...
Queue waitQueue;
explicit Connection(const sp& inputChannel,
const sp& inputWindowHandle, bool monitor);
...
};
这里,一个Connection 对象被创建出来,这个Connection表示客户端和服务端的一个输入数据通道,每个Connection都对应一个服务端的InputChannel,每个服务端的InputChannel又对应一个socket pair的fd。InputDispatcher用fd为索引,将所有的Connection保存在mConnectionByFd中。再将这个fd注册在InputDispatcher的Looper的监控列表里,这样一旦对端的socket写入数据,Looper就会被唤醒,接着就会调用回调函数handleReceiveCallback。另外,一个Dispatcher可能有多个Connection(多个Window)同时存在。
2)client端获取客户端的socket fd
inputChannels[1].transferTo(outInputChannel);
这个outInputChannel是客户端setView创建的,当参数传递过来的。
这里很明显就是将client端的fd传给outInputChannel,而AMS与应用通信是通过binder,因此此处fd是通过binder传递到应用。
然后在setView中会创建WindowInputEventReceiver对象,构造方法会调用super,在其父类InputEventReceiver的构造方法中会执行nativeInit,最终执行如下代码:
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
//将socket客户端的fd添加到主线程的消息池
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
此处的Looper便是应用主线程的Looper,将socket客户端的fd添加到应用线程的Looper来监听,回调方法为NativeInputEventReceiver。
至此,socket正式建立连接。
两端连接建立的调用流程:
其实这部分还是比较复杂的,下面来捋一下关系:
ViewRootImpl setView的时候binder调用执行WMS的addWindow方法,主要任务是通过openInputChannelPair来创建socket pair,对应一对fd。
注册fd:
socket服务端fd保存到system_server中的WindowState的mInputChannel;
socket客户端fd通过binder传回到远程进程的UI主线程ViewRootImpl的mInputChannel;
两端都通过各自Looper监听了对端的写操作,一旦对端搞事情,马上回调响应。
回调:
服务端:收到消息后回调InputDispatcher.handleReceiveCallback()。
客户端:收到消息后回调NativeInputEventReceiver.handleEvent()。
客户端其实相当比较简单,通过binder拿到socket fd 加到主线程looper中,epoll wait等待服务端Dispatcher给input event。但是客户端这边稍微复杂一点点,有如下几个类需要捋一下关系:
InputChannel:包含了channelName和对应端socket fd的信息,以及包括了发送和接收消息的功能封装。
Connection:描述的是一个连接通道,主要包含:服务端的inputChannel 、outboundQueue以及waitQueue。它属于一个连接之后数据操作的渠道。
mConnectionsByFd 对Connection的统一管理,以服务端fd为key,Connection为value,当Dispatcher线获取到事件,会按目前focusWindow对应的应用程序,从mConnectionsByFd(KeyedVector)中找出Connection中对应的fd,然后把事件放入其中,那么客户端的fd马上就能收到事件。
整个通信流程总结:
1)那么InputDispatcher通过focusWindow的fd获取到对应的connection,将DipatcherEntry放入outboundQueue中,InputDispatcher线程调用InputPublisher的publishKeyEvent向应用主线程发送input事件,然后对应事件从outboundQueue移到waitQueue,这个过程是异步的不用等待。
2)应用主线程收到消息后回调NativeInputEventReceiver.handleEvent()接收到该事件,调用InputConsumer的consumeEvents来处理该事件, 一路执行到ViewRootImpl.deliverInputEvent()方法。
3)应用程序事件分发完成后,则会执行finishInputEvent()方法.再进一步调用InputConsumer::sendFinishedSignal 告知InputDispatcher线程该时事件已处理完成。
4)InputDispatcher线程收到该事件后, 执行InputDispatcher::handleReceiveCallback();最终会调用doDispatchCycleFinishedLockedInterruptible()方法 ,将dispatchEntry事件从等待队列(waitQueue)中移除.
最后再来思考一个问题:为什么system_server与应用程序进程之前的通信选择socket而不是binder呢?看过一个比较好的解释的版本:
Socket可以实现异步的通知,且只需要两个线程参与(Pipe两端各一个),假设系统有N个应用程序,跟输入处理相关的线程数目是 n+1 (1是发送(Input Dispatcher)线程)。然而,如果用Binder实现的话,为了实现异步接收,每个应用程序需要两个线程,一个Binder线程,一个后台处理线程,(不能在Binder线程里处理输入,因为这样太耗时,将会堵塞住发送端的调用线程)。在发送端,同样需要两个线程,一个发送线程,一个接收线程来接收应用的完成通知,所以,N个应用程序需要 2(N+1)个线程。两种都能满足异步通知,但是明显socket需要的线程明显少于binder,因此选择socket更为高效。
自此,两端的连接过程就分析到这。
下一篇文章:
Android Input(六)-ViewRootImpl接收事件
参考:
https://www.cnblogs.com/samchen2009/p/3368158.html
http://gityuan.com/2016/12/24/input-ui/