Android系统源码阅读(12)(InputChannel的注册过程)

博观而约取,厚积而薄发。这篇文章主要讲述Android系统源码阅读(12):InputChannel的注册过程相关的知识,希望能为你提供帮助。
android系统源码阅读(12):InputChannel的注册过程

请对照AOSP版本:6.0.1_r50。
InputManager可以获得输入事件并分发,Activity需要处理这些输入事件。那么,这两者之间如何建立的连接呢?这就需要InputChannel作为桥梁建立两者之间的通道。
1. ViewRootImpl创建InputChannel这里ViewRoot类已经消失了,由ViewRootImpl替代。Activity在创建时会将自己的DecorView设置给对应的ViewRootImpl。
Android系统源码阅读(12)(InputChannel的注册过程)

文章图片

1.1
这一步会创建client的InputChannel,并且将当前启动的Activity的窗口传递给WindowManagerService。
frameworks/base/core/java/android/view/ViewRootImpl.java
/** * We have one child */ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { //将view设置为传入的DecorView mView = view; //..mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; *///新建InputChannel if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); //mWindowSession是一个Binder代理对象 //它引用了运行在WindowManagerService中的一个类型为Session的Binder本地对象 //向WindowManagerService添加正在启动的Activity的窗口 //这里还会将InputChannel传递过去 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { //.. } finally { //.. } //..if (view instanceof RootViewSurfaceTaker) { mInputQueueCallback = ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } //将InputChannel和主线程关联起来,在下面会详细讲解 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } view.assignParent(this); } } }

1.2
这一步是进程间的请求,从应用进程转到WindowManagerService进程,对应于1.1中的函数addToDisplay。这里会补足一些参数,开始调用下一步函数。
1.3
这一步也是调整一些参数,然后交给WindowManagerService来处理。
1.4
这里会将传入的Window存入Map来进行统一管理,同时创建了一对server/client端的InputChannel。
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) {//先对添加的window做一些检查,省略..//创建了一个WindowSatete对象 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(); //创建了一个InputChannel对,在1.5中详细讲解 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); //一个放在WindowState里作为server端的InputChannel win.setInputChannel(inputChannels[0]); //一个转化为client传递过来的outInputChannel inputChannels[1].transferTo(outInputChannel); //从上一篇文章中的1.1可知,InputManager作为参数传入 //WindowManagerService的构造函数,并且存放在mInputManager中 //下面章节会详细讲述如何注册server端的InputChannel mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); }// From now on, no exceptions or errors allowed! //将win放入mWindowMap,以client的binder为关键字 mWindowMap.put(client.asBinder(), win); //将win加入相应的list,省略..mInputMonitor.setUpdateInputWindowsNeededLw(); boolean focusChanged = false; if (win.canReceiveKeys()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false /*updateInputWindows*/); if (focusChanged) { imMayMove = false; } }if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); }assignLayersLocked(displayContent.getWindowList()); // Don‘t do layout here, the window must call // relayout to be displayed, so we‘ll do it there.if (focusChanged) { mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/); } mInputMonitor.updateInputWindowsLw(false /*force*/); }return res; }

1.5
这一步会将任务交给c++层来处理。
1.6
这一步对应的c++的函数为android_view_InputChannel_nativeOpenInputChannelPair,它会创建两个InputChannel,并返回。
frameworks/base/core/jni/android_view_InputChannel.cpp :
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, jclass clazz, jstring nameObj) { //将java String变为char* const char* nameChars = env-> GetStringUTFChars(nameObj, NULL); String8 name(nameChars); env-> ReleaseStringUTFChars(nameObj, nameChars); sp< InputChannel> serverChannel; sp< InputChannel> clientChannel; //创建c++层的两个Channel,将在下一步详细讲解 status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); jobjectArray channelPair = env-> NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); //创建java的server channel jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(serverChannel)); //创建java的client channel jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, new NativeInputChannel(clientChannel)); //存入java数组 env-> SetObjectArrayElement(channelPair, 0, serverChannelObj); env-> SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair; }

1.7
注意,这一步真的要创建ChannelPair了。
frameworks/native/libs/input/InputTransport.cpp :
status_t InputChannel::openInputChannelPair(const String8& name, sp< InputChannel> & outServerChannel, sp< InputChannel> & outClientChannel) { int sockets[2]; //这个socketpair建立的是一个可以双向通信的管道,创建一对套接字描述符 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; ALOGE("channel ‘%s‘ ~ Could not create socket pair.errno=%d", name.string(), errno); outServerChannel.clear(); outClientChannel.clear(); return result; }//设置管道缓存的大小 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)); //开始new 两个InputChannel String8 serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; }

socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组s[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在s[1]中写入,然后从s[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从1读取,仍然阻塞;反之亦然。该段解释来自。
这里new了两个InputChannel,该类的构造函数如下:
frameworks/native/libs/input/InputTransport.cpp :
InputChannel::InputChannel(const String8& name, int fd) : mName(name), mFd(fd) { //.. int result = fcntl(mFd, F_SETFL, O_NONBLOCK); //.. }

这里将描述符mFd设置为nonblock。
显然,这里和Android 2.3版本有着很大区别。在旧版本中使用的是匿名共享内存和两个pipe来实现双向的通信。显然,新版本利用的linux系统的新机制,更为简洁高效。
以上步骤实在新Activity建立,窗口开始创建时执行的。这里主要就让WindowManagerService建立一对InputChannel,将Activity的Window和InputDispatcher建立起连接,从而传输输入事件。
2. Server端注册InputChannel在1.4中,创建了一对InputChannel,其中Server端的InputChannel会注册进InputManagerService。
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);

Android系统源码阅读(12)(InputChannel的注册过程)

文章图片

2.1
这一步就检验了一下传入的InputChannel是否为空。下面一言不合就开始调用native函数。
2.2
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
//将指针转化为NativeInputManager NativeInputManager* im = reinterpret_cast< NativeInputManager*> (ptr); //将java的InputChannel对象转化为一个c++层的InputChannel对象 sp< InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); //将InputWindowHandle转化为c++层的InputWindowHandler sp< InputWindowHandle> inputWindowHandle = android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); //将注册任务交给NativeInputManager status_t status = im-> registerInputChannel(env, inputChannel, inputWindowHandle, monitor); //..

2.3
这一步通过NativeInputManager中的InputManager,InputManager通过InputDispatcher来注册InputChannel。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
mInputManager-> getDispatcher()-> registerInputChannel( inputChannel, inputWindowHandle, monitor);

2.4
终于,将Server端的InputChannel交给了InputDispatcher。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
AutoMutex _l(mLock); //判断是否该InputChannel已经添加过 if (getConnectionIndexLocked(inputChannel) > = 0) { ALOGW("Attempted to register already registered input channel ‘%s‘", inputChannel-> getName().string()); return BAD_VALUE; } //创建一个Connection sp< Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); //获得InputChannel的文件描述符 int fd = inputChannel-> getFd(); //将Connection 以文件描述符为关键字,添加入m mConnectionsByFd.add(fd, connection); //.. //将文件描述符添加Looper,让Looper监控InputChannel的IO事件 //当IO事件发生时,就会调用回调函数handleReceiveCallback mLooper-> addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

到这一步,InputDispatcher已经将InputChannel的Server端管理起来了。当Dispatcher将一个事件通过Channel发送给应用程序窗口以后,会进入休眠状态,直到应用窗口再次通过这个Channel返回一个消息将其激活,Dispatcher才准备发送下一个消息。
这里创建的Connection,Connection中又创建了InputPublisher。InputPublisher可以直接将消息通过这个InputChannel发送出去。
3. 向InputManagerService注册当前激活的应用程序窗口再次回顾一下1.4的,在1.4中WindowManagerService在焦点发生改变时,需要改变Focused Window。这里会在InputMonitor中注册当前激活的窗口。
Android系统源码阅读(12)(InputChannel的注册过程)

文章图片

3.1
在InputMonitor中,有mInputFocus保存着当前激活的窗口,这里会将mInputFocus设置为传入的newWindow。然后调用updateInputWindowLW继续更新激活的窗口。
3.2
在1.4中,每个window会被加入一个Window List。这里会遍历这些List中的Windows,然后将这些windows进一步交给InputManagerService处理。
rameworks/base/services/core/java/com/android/server/wm/InputMonitor.java :
// Populate the input window list with information about all of the windows that // could potentially receive input. // As an optimization, we could try to prune the list of windows but this turns // out to be difficult because only the native code knows for sure which window // currently has touch focus. //..// Add all windows on the default display. //mService指WindowManagerService final int numDisplays = mService.mDisplayContents.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList(); for (int winNdx = windows.size() - 1; winNdx > = 0; --winNdx) { final WindowState child = windows.get(winNdx); final InputChannel inputChannel = child.mInputChannel; final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; if (inputChannel == null || inputWindowHandle == null || child.mRemoved) { // Skip this window because it cannot possibly receive input. continue; }//..final int flags = child.mAttrs.flags; final int privateFlags = child.mAttrs.privateFlags; final int type = child.mAttrs.type; //只有一个window可以获得Focus final boolean hasFocus = (child == mInputFocus); final boolean isVisible = child.isVisibleLw(); //.. addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper); } }// Send windows to native code. //将这些windows交给InputManager继续进行注册 mService.mInputManager.setInputWindows(mInputWindowHandles); // Clear the list in preparation for the next round. clearInputWindowHandlesLw();

3.3
这一步InputManagerService将任务交给了c++层。
3.4
进入c++层,将这些windowHandles交给NativeInputManager。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, jlong ptr, jobjectArray windowHandleObjArray) { //将ptr转化为指针 NativeInputManager* im = reinterpret_cast< NativeInputManager*> (ptr); //windowHandleObjArray是传入的windowHandles im-> setInputWindows(env, windowHandleObjArray); }

3.5
这一步先将java的windowHandle变为c++对象,然后这些windowHandle被交给InputDispatcher处理。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :
if (windowHandleObjArray) { jsize length = env-> GetArrayLength(windowHandleObjArray); //将java对象转化为c++对象 for (jsize i = 0; i < length; i++) { jobject windowHandleObj = env-> GetObjectArrayElement(windowHandleObjArray, i); if (! windowHandleObj) { break; // found null element indicating end of used portion of the array }sp< InputWindowHandle> windowHandle = android_server_InputWindowHandle_getHandle(env, windowHandleObj); if (windowHandle != NULL) { windowHandles.push(windowHandle); } env-> DeleteLocalRef(windowHandleObj); } }//将这些windowHandles交给Dispatcher处理 mInputManager-> getDispatcher()-> setInputWindows(windowHandles);

3.6
InputDispatcher跟新WindowHandle,并且更新Focused window。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
{ // acquire lock AutoMutex _l(mLock); //清理旧的WindowHandle Vector< sp< InputWindowHandle> > oldWindowHandles = mWindowHandles; mWindowHandles = inputWindowHandles; sp< InputWindowHandle> newFocusedWindowHandle; bool foundHoveredWindow = false; //找到新的Focused WindowHandle for (size_t i = 0; i < mWindowHandles.size(); i++) { const sp< InputWindowHandle> & windowHandle = mWindowHandles.itemAt(i); if (!windowHandle-> updateInfo() || windowHandle-> getInputChannel() == NULL) { mWindowHandles.removeAt(i--); continue; } if (windowHandle-> getInfo()-> hasFocus) { newFocusedWindowHandle = windowHandle; } if (windowHandle == mLastHoverWindowHandle) { foundHoveredWindow = true; } }if (!foundHoveredWindow) { mLastHoverWindowHandle = NULL; }if (mFocusedWindowHandle != newFocusedWindowHandle) { //旧的FocusedWindow和新的不同,则需要停止向旧的Channel中发送消息 if (mFocusedWindowHandle != NULL) { sp< InputChannel> focusedInputChannel = mFocusedWindowHandle-> getInputChannel(); if (focusedInputChannel != NULL) { synthesizeCancelationEventsForInputChannelLocked( focusedInputChannel, options); } } //设置新的Focused Window mFocusedWindowHandle = newFocusedWindowHandle; }// Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (size_t i = 0; i < oldWindowHandles.size(); i++) { const sp< InputWindowHandle> & oldWindowHandle = oldWindowHandles.itemAt(i); if (!hasWindowHandleLocked(oldWindowHandle)) { //释放已经不存在的旧Window oldWindowHandle-> releaseInfo(); } } } // release lock// Wake up poll loop since it may need to make new input dispatching choices. mLooper-> wake();

到这里,InputDispatcher获取了和激活的Window的双向通信通道,同时Dispatcher也知道了是哪个Window处于Focused状态。只要Client端将通信通道建立完毕,则Dispatcher可以向Activity的Window发送消息了。
4. Client端注册InputChannel
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());

在1.1中,通过WindowSession添加Window和InputChannel后,mInputChannel对象已经转化为双向通道中的client端通道了,从1.4可以知道。然后,该步骤又创建了一个WindowInputEventReceiver类的对象mInputEventReceiver,它将mInputChannel和主线程绑定在一起。
下面就看一下WindowInputEventReceiver的构造过程。
Android系统源码阅读(12)(InputChannel的注册过程)

文章图片

4.1
WindowInputEventReceiver是InputEventReceiver的子类,具体构造过程在InputEventReceiver中。
4.2
这一步又开始调用native函数。
frameworks/base/core/java/android/view/InputEventReceiver.java:
mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference< InputEventReceiver> (this), inputChannel, mMessageQueue);

4.3
创建c++层的NativeInputEventReceiver。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { //将java对象转化为c++对象 sp< InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); //传入的java MessageQueue转化为c++的MessageQueue sp< MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); //..//创建了一个NativeInputEventReceiver,构造函数在下面 sp< NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue); //对其进行初始化 status_t status = receiver-> initialize(); //返回一个NativeInputEventReceiver的指针 receiver-> incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object return reinterpret_cast< jlong> (receiver.get()); }

NativeInputEventReceiver的构造函数。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp :
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak, const sp< InputChannel> & inputChannel, const sp< MessageQueue> & messageQueue) : mReceiverWeakGlobal(env-> NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false), mFdEvents(0) { if (kDebugDispatchCycle) { ALOGD("channel ‘%s‘ ~ Initializing input event receiver.", getInputChannelName()); } }

4.4
这一步调用函数setFdEvents,参数为ALOOPER_EVENT_INPUT
4.5
将传入的Channel让主线程监听起来,以便处理传入的消息。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {‘ if (mFdEvents != events) { mFdEvents = events; //获取Channel的文件描述符 int fd = mInputConsumer.getChannel()-> getFd(); if (events) { //获取Looper,给其添加监听fd的IO事件的请求 //MessageQueue的Looper为主线程Looper,如果Looper进入睡眠 //则会被Channel上写入的事件唤醒,从而可以处理新来的消息 mMessageQueue-> getLooper()-> addFd(fd, 0, events, this, NULL); } else { mMessageQueue-> getLooper()-> removeFd(fd); } } }

这里监听的文件描述符发生IO事件时,调用的回调函数就是NativeInputEventReceiver自己,因为它为LooperCallback子类。
同时注意这里的Looper就是应用主线程的Looper,这里向其epoll多添加了一个文件描述符进行监听,因为epoll可以同时监听多个epoll的IO事件。同时设定了监听的类型为ALOOPER_EVENT_INPUT。
回忆下第10章的1.6步骤中epoll_wait进入的睡眠后,会监听mWakeEventFd文件描述符的事件。不仅如此,这一步还会监听更多的文件描述符的事件,这里就包括建立的InputChannel的文件描述符。所以在主线程通过调用pollInner进入睡眠以后,被唤醒后会判断发生事件的文件描述符是哪一个。
system/core/libutils/Looper.cpp :
if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex > = 0) { int events = 0; if (epollEvents & EPOLLIN) events |= EVENT_INPUT; if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } }

【Android系统源码阅读(12)(InputChannel的注册过程)】到这里,InputChannel在Client端也进行了监听,整个完整的window所在的应用主线程和InputDispatcher线程之间的双向Channel已经建立完毕,同时InputDispatcher知道哪一个window处于激活状态,因为它知道向哪一个window发送消息。


    推荐阅读