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);
}