InputChannel
应用程序窗口需要获取输入事件,就需要和IMS建立一个传输通道(WMS替我们做了),这个通道就是InputChannel。
InputChannel本质就是local unix domain socket,创建时,用到了socketpair函数,其作用见man手册。
man socketpair
应用程序调用和InputChannel产生联系的函数调用流程如下:
//ViewRootImpl.java
void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
}
//Session.java 前者通过Binder 最终到这里
public 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);
}
// 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) {
...
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
....
}
win的实际类型是WindowState,通过其openInputChannel方法,我们会注册一个InputChannel到IMS,同时返回一个到outInputChannel变量。通过outInputChannel,我们就能从IMS获取到输入事件,同时也能发送数据到IMS。
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
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);
}
openInputChannelPair返回两个InputChannel,可以将其看做是全双工的匿名管道(底层实现是socketpair)。将其中一个返回给ViewRootImpl::mInputChannel,将另一个注册到IMS,这样,ViewRootImpl就能够处理输入事件了。
pipe是半双工的,返回的两个fd,一个用于读,一个用于写。
mkfifo,命名管道,创建一个文件,使用者打开文件,然后对其读写。(全双工?)
InputChannel创建
通过静态成员函数openInputChannelPair创建。
// InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {
...
return nativeOpenInputChannelPair(name);
}
//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);
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
//注意这里,用 InputChannel 来构建了一个NativeInputChannel
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
创建两个NativeInputChannel对象,然后初始化两个Java InputChannel对象。
Java InputChannel有一个long mPtr成员变量,其保存着NativeInputChannel(内部持有InputChannel的引用)的内存地址,通过这个地址,后续通过JNI进入Naitve时,就能找到这里创建的InputChannel了。
Native InputChannel的创建
也是通过静态成员函数(openInputChannelPair)创建。
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;
}
// Socket buffer size. The default is typically about 128KB, which is much larger than
// we really need. So we make it smaller. It just needs to be big enough to hold
// a few dozen large multi-finger motion events in the case where an application gets
// behind processing touches.
//static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
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;
}
socketpair返回的两个文件描述符,对我们来说是无法区分的,所以,这里 分别为其指定了一个名称,用来区分其作用。创建完了,接下来就是注册到IMS了。
注册 InputChannel到 IMS
前面的WindowState中,在创建了InputChannel后,会调用registerInputChannel将其注册到IMS中。
WindowState在WMS中创建,IMS和WMS都位于SystemServer进程,所以,这里可以直接调用IMS::registerInputChannel进行注册。
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
// mPtr对应的是 NativeInputManager
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
既然是通过JNI进入Native,那么直接看一下NativeInputManager::registerInputChannel的实现:
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
Oh!!! 去到了InputDispatcher,也是能够理解的,毕竟事件的分发就是由InputDispatcher完成的。。。
一开始看到InputDispatcher的构造函数中出现了Looper还有点懵,现在知道了。。
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
最后就是将InputChannel保存到了Connection中,然后InputDispatcher中持有一个:
//InputDispatcher.cpp
// All registered connections mapped by channel file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByFd;
数据发送和接收
InputChannel的数据发送和接收对应两个函数:
tatus_t InputChannel::sendMessage(const InputMessage* msg) {
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
return DEAD_OBJECT;
}
return -error;
}
if (size_t(nWrite) != msgLength) {
return DEAD_OBJECT;
}
return OK;
}
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
int error = errno;
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
return DEAD_OBJECT;
}
return -error;
}
if (nRead == 0) { // check for EOF
return DEAD_OBJECT;
}
if (!msg->isValid(nRead)) {
}
return OK;
}
为什么贴这啷个函数呢?
我是觉得对于数据send/recv这两个函数可以直接copy过来用,对于错误信息的处理,比较完善了,
我平时也只会处理一个EAGAIN。
InputWindowHandle
前面WindowState::openInputChannel中注册InputChannel到IMS时,还有一个InputWindowHandle,后面到InputDispatcher中,也用到了这个。他的作用是什么??
InputWindowHandle创建
// WindowState的构造函数中创建
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
getDisplayId());
// 构造函数
public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
Object windowState, IWindow clientWindow, int displayId) {
this.inputApplicationHandle = inputApplicationHandle;
this.windowState = windowState;
this.clientWindow = clientWindow;
this.displayId = displayId;
}
这里主要介绍一下
IWindow,由AIDL生成。对WMS而言,其就是Window的客户端,WMS通过IWindow就通知Window有事件发生。oneway interface IWindow { /** * Drag/drop events */ void dispatchDragEvent(in DragEvent event); /** * Tell the window that it is either gaining or losing focus. Keep it up * to date on the current state showing navigational focus (touch mode) too. */ void windowFocusChanged(boolean hasFocus, boolean inTouchMode); ... }
NativeInputWindowHandle
继承至InputWindowHandle。
主要有两个成员变量。
InputWindowInfo* mInfo; //InputWindowHandle
jweak mObjWeak; //NativeInputWindowHandle
InputWindowInfo中的很多成员变量和Java端的InputWindowHandle是对应的。mObjWeak就是对Java端的InputWindowHandle的一个弱引用。
通过InputWindowHandle::updateInfo就能够更新mInfo中的成员值(通过jni获取java端对象的成员值)。
废话这么多,还是不晓得这个东东是干嘛用的,大胆猜测一下,通过这个,我们能判断事件需要转发给那些窗口处理!!待补充。。。。
总结
InputChannel和InputWindowHandle从Java层到JNI再到Native层,大概的关系如下:

WMS中创建两个InputChannel(Java层定义)对象后,一个返回给调用端,一个注册到IMS(终点就是InputDispatcher),同时注册下来的还有InputWindowHandle。
- 返回给
Window的InputChannel对应的Native类型是NativeInputChannel(JNI中定义)。 - 注册到
IMS中的实际类型就是InputChannel(libinput中定义)。
在InputDispatcher::registerInputChannel中,注册的InputChannel和InputWindowHandle都保存到了Connection对象中。Connection对象最终又保存到了InputDispatcher::mConnectionsByFd中。
原本在看InputDispatcher的时候好奇InputChannel是个什么玩意,结果,就入了这个坑,记录一下。。。
疑问
NativeInputChannel的作用是啥???直接使用InputChannel不行吗???
包了那么多,最终也注册到IMS的还是InputChannel。
补充
InputChannel在哪里接收数据
注册到InputDispatcher时,有将InputChannel::mFd添加到Looper中。
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
当往ViewRootImpl::mInputChannel写入数据时,Looper就会执行InputDispatcher::handleReceiveCallback进行处理。
InputChannel在哪里发送数据
答案就是InputPublisher。
class InputPublisher {
public:
/* Creates a publisher associated with an input channel. */
explicit InputPublisher(const sp<InputChannel>& channel);
*/
status_t publishKeyEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
private:
sp<InputChannel> mChannel;
};
看一下前面的Connection类的成员变量:
sp<InputChannel> inputChannel; // never null
sp<InputWindowHandle> inputWindowHandle; // may be null
InputPublisher inputPublisher;
在Connection的构造函数中,会使用inputChannel来初始化InputPublisher::mChannel(InputDispatcher::registerInputChannel中完成)。
然后,当InputDispatcher需要分发按键事件时,就会通过Connection::inputPublisher::publishKeyEvent来完成。
status_t InputPublisher::publishKeyEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
//完成数据发送工作
return mChannel->sendMessage(&msg);
}