本文主要是介绍Android 源码 输入系统之 InputChannel 通信通道建立,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
上一节完成了焦点窗口关联,现在可以分析如何将输入事件继续从 InputDispatcher 继续分发。InputChannel 的 sendMessage 将消息发送出去。实际是调用 socket 的 send 接口来发送消息的。具体一点其实使用的是 socketpair。所以我们先来学习 Linux 如何使用 socketpair,然后“破解” InputChannel 通信,最后再去分析输入事件窗口分发。
一、socketpair 使用
用于创建一对无名的、相互连接的套接字。
#include <sys/types.h>
#include <sys/socket.h>int socketpair(int domain, int type, int protocol, int sv[2]);
参数
domain: 协议家族
type: 套接字类型
protocol: 协议类型
sv: 返回套接字对
返回值
成功返回 0,失败返回 -1
UNIX 域套接字用于在同一台计算机上运行的进程之间的通信。虽然因特网域套接字可用于 同一目的,但 UNIX 域套接字的效率更高。UNIX 域套接字仅仅复制数据,它们并不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不要产生顺序号,无需发送确认报文。
UNIX 域套接字提供流和数据报两种接口。UNIX 域数据报服务是可靠的,既不会丢失报文也不会传递出错。UNIX 域套接字就像是套接字和管道的混合。可以使用它们面向网络的域套接字接口或者使用 socketpair 函数來创建一对无命名的、相互连接的 UNIX 域套接字。
虽然接口足够通用,允许 socketpair 用于其他域,但一般来说操作系统仅对 UNIX 域提供支持。
下面是一个例程,父进程和子进程相互发送消息。父进程调用 sleep 休息 1 秒是为了子进程有机会执行。
#include <sys/types.h>
#include <sys/socket.h>#include <stdlib.h>
#include <stdio.h>int main () {int fd[2];int r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);if (r < 0) {perror("socketpair()");exit(1);}if (fork()) {/* Parent process: echo client */int val = 0;close(fd[1]);while (1) {sleep(1);++val;printf("Parent process Sending data: %d\n", val);write(fd[0], &val, sizeof(val));read(fd[0], &val, sizeof(val));printf("Parent process Data received: %d\n", val);}} else {/* Child process: echo server */int val;close(fd[0]);while (1) {read(fd[1], &val, sizeof(val));printf("Child process Data received: %d\n", val);++val;printf("Child process Sending data: %d\n", val);write(fd[1], &val, sizeof(val));}}
}
运行结果
Parent process Sending data: 1
Child process Data received: 1
Child process Sending data: 2
Parent process Data received: 2
Parent process Sending data: 3
Child process Data received: 3
Child process Sending data: 4
Parent process Data received: 4
Parent process Sending data: 5
Child process Data received: 5
Child process Sending data: 6
Parent process Data received: 6
Parent process Sending data: 7
Child process Data received: 7
Child process Sending data: 8
Parent process Data received: 8
Parent process Sending data: 9
Child process Data received: 9
Child process Sending data: 10
Parent process Data received: 10
Parent process Sending data: 11
Child process Data received: 11
Child process Sending data: 12
Parent process Data received: 12
Parent process Sending data: 13
Child process Data received: 13
Child process Sending data: 14
Parent process Data received: 14
Parent process Sending data: 15
Child process Data received: 15
Child process Sending data: 16
Parent process Data received: 16
......
二、InputChannel 通信
回顾《Android 源码 输入系统之窗口关联》一节,InputChannel 通信得从 ViewRootImpl 类 setView 继续分析。
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {......public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView == null) {mView = view;......if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {// 创建客户端 InputChannel 对象mInputChannel = new InputChannel();}try {......// Binder 机制调用 Session 对象 addToDisplay 方法,注意入参包括 InputChannel 对象res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mInputChannel);} catch (RemoteException e) {......} finally {......}......if (mInputChannel != null) {if (mInputQueueCallback != null) {// 创建 InputQueue 对象mInputQueue = new InputQueue();mInputQueueCallback.onInputQueueCreated(mInputQueue);}// 接收输入事件,创建 WindowInputEventReceiver 对象mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}......}}}......
}
输入通道(InputChannel)指定用于将输入事件发送到另一个进程中的窗口的文件描述符。它是可打包的,因此可以将其发送到接收事件的进程。每次只应该有一个线程从 InputChannel 中读取数据。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {......// 创建一个未初始化的输入通道。// 它可以通过从一个 Parcel 读取数据或将另一个输入通道的状态传输到这个输入通道来初始化。public InputChannel() {}......
}
Session 类 addToDisplay 方法实际工作由 WindowManagerService 类 addWindow 方法完成。
frameworks/base/services/core/java/com/android/server/wm/Session.java
final class Session extends IWindowSession.Stubimplements IBinder.DeathRecipient {final WindowManagerService mService;......@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);}......
}
- 创建 InputChannel 对
- 将服务端 InputChannel 赋给服务端的 WindowState
- 将客户端 InputChannel 传递到 outInputChannel, 最终返回客户端应用进程
- 将服务端 InputChannel 注册到 InputDispatcher
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {......final InputMonitor mInputMonitor = new InputMonitor(this);......public int addWindow(Session session, IWindow client, int seq,WindowManager.LayoutParams attrs, int viewVisibility, int displayId,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,InputChannel outInputChannel) {......synchronized(mWindowMap) {......// 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();// 创建 InputChannel 对InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);// 将服务端 InputChannel 赋给服务端的 WindowStatewin.setInputChannel(inputChannels[0]);// 将客户端 InputChannel 传递到 outInputChannel, 最终返回客户端应用进程inputChannels[1].transferTo(outInputChannel);// 将服务端 InputChannel 注册到 InputDispatcher mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);}......}......}......
}
创建 InputChannel 对,实际是使用 socketpair 来完成的。openInputChannelPair 简单做了 name 判空后,实际工作由 nativeOpenInputChannelPair 方法完成。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {......private static native InputChannel[] nativeOpenInputChannelPair(String name);......public static InputChannel[] openInputChannelPair(String name) {if (name == null) {throw new IllegalArgumentException("name must not be null");}if (DEBUG) {Slog.d(TAG, "Opening input channel pair '" + name + "'");}return nativeOpenInputChannelPair(name);}......
}
不难看出 nativeOpenInputChannelPair 只是一个 jni 接口,实际工作由 jni native 层对应函数(android_view_InputChannel_nativeOpenInputChannelPair)完成。
- name 转化(从 java String 对象转化为 C++ String8)
- 调用 Native InputChannel 对象 openInputChannelPair 方法
- 构建 NativeInputChannel 对象,并将 NativeInputChannel 对象转化为 Java InputChannel 对象返回
注意 Native InputChannel 对象和 NativeInputChannel 对象,前者为 C++ 类 InputChannel,后者为 C++ 类 NativeInputChannel。
frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,jclass clazz, jstring nameObj) {const char* nameChars = env->GetStringUTFChars(nameObj, NULL);String8 name(nameChars);env->ReleaseStringUTFChars(nameObj, nameChars);sp<InputChannel> serverChannel;sp<InputChannel> clientChannel;status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);if (result) {String8 message;message.appendFormat("Could not open input channel pair. status=%d", result);jniThrowRuntimeException(env, message.string());return NULL;}jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);if (env->ExceptionCheck()) {return NULL;}jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,new NativeInputChannel(serverChannel));if (env->ExceptionCheck()) {return NULL;}jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,new NativeInputChannel(clientChannel));if (env->ExceptionCheck()) {return NULL;}env->SetObjectArrayElement(channelPair, 0, serverChannelObj);env->SetObjectArrayElement(channelPair, 1, clientChannelObj);return channelPair;
}
- 调用 socketpair 创建一对无名的、相互连接的套接字
- setsockopt() 函数,用于任意类型、任意状态套接口的设置选项值
SO_RCVBUF int 为接收确定缓冲区大小。
SO_RCVBUF int 为接收确定缓冲区大小。
- 分别创建服务端和客户端 Native InputChannel 对象
frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const String8& name,sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {int sockets[2];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));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;
}
NativeInputChannel 对象将传递来的 InputChannel 指针保存在自己的成员变量 mInputChannel 中。
frameworks/base/core/jni/android_view_InputChannel.cpp
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :mInputChannel(inputChannel), mDisposeCallback(NULL) {
}
再来分析 android_view_InputChannel_createInputChannel 函数。
- 调用 jni 方法 NewObject 创建一个 Java InputChannel 对象
- 调用 android_view_InputChannel_setNativeInputChannel 将 nativeInputChannel 对象指针强转为 jlong,然后将其设置到 Java InputChannel 类 mPtr 成员上。
frameworks/base/core/jni/android_view_InputChannel.cpp
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,NativeInputChannel* nativeInputChannel) {jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,gInputChannelClassInfo.ctor);if (inputChannelObj) {android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);}return inputChannelObj;
}
InputChannel 类 transferTo 函数,将 InputChannel 内部状态的所有权转移到另一个实例,并使该实例无效。这用于在 binder 调用中将输入通道作为 out 参数传递。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {......public void transferTo(InputChannel outParameter) {if (outParameter == null) {throw new IllegalArgumentException("outParameter must not be null");}nativeTransferTo(outParameter);}......
}
otherObj 即对应的入参,调用 android_view_InputChannel_getNativeInputChannel 方法可将 Java InputChannel 对象的 mPtr 成员变量强转为 NativeInputChannel 对象,由于传递过来的 Java InputChannel 对象是个“空壳”,所以强转以后一定为空,否则抛出异常。
接下来调用 android_view_InputChannel_getNativeInputChannel 将 obj 转化为 NativeInputChannel 对象。并将其(实际是指针)设置到 otherObj mPtr 成员变量上。
最后将 obj mPtr 成员变量设置为 0,即表示 NativeInputChannel 对象为 NULL。
frameworks/base/core/jni/android_view_InputChannel.cpp
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,jobject otherObj) {if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {jniThrowException(env, "java/lang/IllegalStateException","Other object already has a native input channel.");return;}NativeInputChannel* nativeInputChannel =android_view_InputChannel_getNativeInputChannel(env, obj);android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
下面是 android_view_InputChannel_getNativeInputChannel 和 android_view_InputChannel_setNativeInputChannel 函数,主要调用 jni 方法 GetLongField 和 SetLongField,实现 Native 层操作 java 对象成员变量。
frameworks/base/core/jni/android_view_InputChannel.cpp
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,jobject inputChannelObj) {jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);return reinterpret_cast<NativeInputChannel*>(longPtr);
}static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,NativeInputChannel* nativeInputChannel) {env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,reinterpret_cast<jlong>(nativeInputChannel));
}
回到 WindowManagerService addWindow 方法,继续分析将服务端 InputChannel 注册到 InputDispatcher 的过程。registerInputChannel 函数实际调用了 nativeRegisterInputChannel 完成注册。
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,InputWindowHandle inputWindowHandle, boolean monitor);......public void registerInputChannel(InputChannel inputChannel,InputWindowHandle inputWindowHandle) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null.");}nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);}......
}
- 将 ptr 强转为 NativeInputManager 对象
- 将 inputChannelObj jobject 转化为 Native InputChannel 对象
- 将 inputWindowHandleObj jobject 转化为 Native InputWindowHandle 对象
- 调用 NativeInputManager 类 registerInputChannel 方法完成实际注册工作
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);if (inputChannel == NULL) {throwInputChannelNotInitialized(env);return;}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;}......
}
NativeInputManager 类 registerInputChannel 方法最终调用 InputDispatcher 类的 registerInputChannel 完成真正的注册。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle, monitor);
}
- 检查 InputChannel 是否已经注册过
- 创建 Connection 对象,他表示客户端和服务端的一个输入数据通道
- 以 fd 为 key 将 Connection 添加到容器(mConnectionsByFd)中
- 将 fd 添加到 Looper 监听列表中。一旦对端的 Socket 写入数据,Looper 就会被唤醒,接着就会调用 handleReceiveCallback
frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {......{ // acquire lockAutoMutex _l(mLock);// 进入此 case 表示 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);// 获取 socketpair fdint fd = inputChannel->getFd();// 以 fd 为 key 将 Connection 添加到容器中mConnectionsByFd.add(fd, connection);......// 将 fd 添加到 Looper 监听列表中,一旦对端的 socket 写入数据,Looper 就会被唤醒// 接着就会调用 handleReceiveCallbackmLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);} // release lock// 唤醒 Looper,因为一些连接(Connection)已经改变mLooper->wake();return OK;
}
代码走到这里,InputDispatcher 就和 InputChannel 建立了关联。现在是时候回到 ViewRootImpl setView 方法中具体分析如何关联客户端 InputChannel。马上来分析创建 WindowInputEventReceiver 对象流程。
在 WindowInputEventReceiver 构造器中调用了 InputEventReceiver 构造器。
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {......final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}@Overridepublic void onInputEvent(InputEvent event) {enqueueInputEvent(event, this, 0, true);}@Overridepublic void onBatchedInputEventPending() {if (mUnbufferedInputDispatch) {super.onBatchedInputEventPending();} else {scheduleConsumeBatchedInput();}}@Overridepublic void dispose() {unscheduleConsumeBatchedInput();super.dispose();}}......
}
InputEventReceiver 构造器中首先检查入参 inputChannel 和 looper 是否为 null,接着从 looper 中获取 MessageQueue (mMessageQueue),最后调用 nativeInit 进一步初始化。
frameworks/base/core/java/android/view/InputEventReceiver.java
public abstract class InputEventReceiver {......public InputEventReceiver(InputChannel inputChannel, Looper looper) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null");}if (looper == null) {throw new IllegalArgumentException("looper must not be null");}mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);mCloseGuard.open("dispose");}......
}
- 转化 Java InputChannel 对象为 Native InputChannel 对象
- 转化 Java MessageQueue 对象为 Native MessageQueue 对象
- 构建 NativeInputEventReceiver 对象
- 调用 NativeInputEventReceiver 对象 initialize() 方法初始化
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);if (inputChannel == NULL) {jniThrowRuntimeException(env, "InputChannel is not initialized.");return 0;}sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == NULL) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();if (status) {String8 message;message.appendFormat("Failed to initialize input event receiver. status=%d", status);jniThrowRuntimeException(env, message.string());return 0;}receiver->incStrong(gInputEventReceiverClassInfo.clazz); // 保留对象的引用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());}
}
initialize() 函数仅仅调用了 setFdEvents() 函数,然后直接返回 OK。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::initialize() {setFdEvents(ALOOPER_EVENT_INPUT);return OK;
}
由于 mFdEvents 初始化为 0, ALOOPER_EVENT_INPUT = 1 << 0,因此会赋值 mFdEvents = ALOOPER_EVENT_INPUT。然后取出 socket fd,就会向客户端 Looper 添加 fd 描述符,当有数据从服务端写入,就会唤醒 Looper。最终回调 NativeInputEventReceiver 的 handleEvent 方法。
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, NULL);} else {mMessageQueue->getLooper()->removeFd(fd);}}
}
老规矩时序图作为最后的总结。
这篇关于Android 源码 输入系统之 InputChannel 通信通道建立的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!