Android IMS原理解析之InputChannel
接着上篇文章Android IMS原理解析之InputDispatcher的分析,本文主要分析Input事件如何发送到对应窗口,事件发送及处理反馈主要是通过InputChannel来进行的,结合Connection、InputPublisher、InputConsumer等,接下来一起分析一下:
Input事件发送到窗口
上面讲到,InputDispatcher在事件派发时最终会通过Connection调用到InputPublisher内部的publishMotionEvent()方法,先看一下Connection这个类,该类定义在InputDispatcher.h中:
1.Connection
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
monitor(monitor),
inputPublisher(inputChannel), inputPublisherBlocked(false) {
}
class Connection : public RefBase {
protected:
virtual ~Connection();
public:
enum Status {
// Everything is peachy.
STATUS_NORMAL,
// An unrecoverable communication error has occurred.
STATUS_BROKEN,
// The input channel has been unregistered.
STATUS_ZOMBIE
};
Status status;
sp<InputChannel> inputChannel; // never null
sp<InputWindowHandle> inputWindowHandle; // may be null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
bool inputPublisherBlocked;
// Queue of events that need to be published to the connection.
Queue<DispatchEntry> outboundQueue;
Queue<DispatchEntry> waitQueue;
explicit Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
const char* getWindowName() const;
const char* getStatusLabel() const;
DispatchEntry* findWaitQueueEntry(uint32_t seq);
}
可以看到,该类内部定义了InputChannel、InputWindowHandle、InputPublisher等变量,是对以上对象的封装,Connection是事件发送的入口,主要的发送逻辑是由封装的类对象执行的;
2.InputPublisher
该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp,看一下publishMotionEvent()的实现:
//创建InputPublisher实例时传入InputChannel
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
mChannel(channel) {
}
status_t InputPublisher::publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source,int32_t displayId,
int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags,int32_t metaState,
int32_t buttonState,float xOffset,float yOffset,float xPrecision,float yPrecision,nsecs_t downTime,
nsecs_t eventTime,uint32_t pointerCount,const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
.....
......
InputMessage msg;
msg.header.type = InputMessage::TYPE_MOTION;
msg.body.motion.seq = seq;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.displayId = displayId;
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
msg.body.motion.edgeFlags = edgeFlags;
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
return mChannel->sendMessage(&msg);
}
在publishMotionEvent()内部,最终是将MotionEvent封装成InputMessage,然后调用mChannel->sendMessage(&msg),mChannel是InputChannel实例;
3.InputChannel
该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp
status_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 BAD_VALUE;
}
return OK;
}
可以看到,sendMessage()和receiveMessage()内部分别对应send()和recv(),从方法名可以看到,是通过socket进行传输的,接下来通过以下几个方面来对InputChannel进行分析:
3.1.InputChannel是什么
InputChannel本质是一对SocketPair(非网络套接字)。SocketPair用来实现在本机内进行进程间的通信。一对SocketPair通过socketpair()函数创建,其使用者可以因此而得到两个相互连接的文件描述符。这两个描述符可以通过套接字接口send()和recv()进行写入和读取,并且向其中一个文件描述符写入的数据,可以从另一个描述符中读取。同pipe()所创建的管道不同,SocketPair的两个文件描述符是双通的,因此非常适合用来进行进程间的交互式通信;
3.2.创建InputChannel
我们知道当一个Window需要显示时,最终都会调用到WMS的addWindow()方法:
//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {
......
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
//仅当窗口的inputFeatures未指定INPUT_FEATURE_NO_INPUT_CHANNEL选项时才会为此窗口创建InputChannel对
final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
......
return res;
}
在内部创建WindowState代表具体的窗口,然后执行openInputChannel()方法:
//WindowState.java
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;
}
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
在openInputChannel()内部执行了三项工作:
1.调用InputChannel的openInputChannelPair()来返回InputChannel[]数组,数组大小为2;
2.将mInputChannel及mInputWindowHandle.inputChannel赋值为inputChannels[0],将mClientChannel赋值为inputChannels[1],然后执行transferTo转换为outInputChannel;
3.执行registerInputChannel();
先看openInputChannelPair()方法:
//InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {
.......
return nativeOpenInputChannelPair(name);
}
该方法会调用nativeOpenInputChannelPair(),即native方法,路径位于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,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
可以看到,会调用 InputChannel::openInputChannelPair来创建serverChannel, clientChannel,然后创建数组channelPair,将serverChannel, clientChannel处理后存入数组,返回给java层,该逻辑实现代码路径为:frameworks/native/libs/input/InputTransport.cpp,一起看一下方法实现:
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
//通过socketpair()函数创建一对无名的相互连接的socket,并保存在sockets数组中
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));
//创建server端的InputChannel对象
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
//创建client端的InputChannel对象
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
用一张流程图总结一下创建InputChannel过程:
3.3.连接到InputDispatcher
前面讲到,在WindowState的openInputChannel()方法中最后会执行mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle)来进行连接,一起看一下调用流程:
//InputManagerService.java
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
在registerInputChannel()方法内部会调用nativeRegisterInputChannel(),会调用到native层,代码路径为: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 (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}
可以看到,在nativeRegisterInputChannel()方法内部会调用到NativeInputManager的registerInputChannel()方法:
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
通过InputManager最终调用到InputDispatcher的registerInputChannel()方法:
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor)
{
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);
}
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
可以看到,在InputDispatcher的registerInputChannel()内部会执行四项工作:
1.根据传入的inputChannel、inputWindowHandle等参数创建Connection实例;
2.获取inputChannel的Fd,然后以键值对形式将fd及connection存入mConnectionsByFd中,后续在进行窗口事件派发时,会从该mConnectionsByFd里面查找到对应的Connection;
3.执行mLooper->addFd,后续fd有数据收到时,会回调handleReceiveCallback方法;
4.调用mLooper->wake();
在前面InputDispatcher的事件派发中代码讲到,会根据目标窗口找到对应的InputWindowHandle,然后根据以下方式来找到对应的Connection,即从mConnectionsByFd中根据fd来匹配到对应的Connection,先注册后发送,前后对应起来了;
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
registerInputChannel()为InputChannel创建了一个Connection,并监听了InputChannel的可读事件,使得InputDispatcher拥有了将事件发送给InputChannel并接受反馈的能力。
用一张流程图总结一下连接InputDispatcher过程:
3.4.连接到窗口
前面分析到,在openInputChannelPair()时会创建一对InputChannel,一个是在addWindow()时通过registerInputChannel()时传入到InputDispatcher,另外一个是用来接收InputDispatcher传来的事件,是在窗口端,接下来一起看一下:
窗口添加的入口是在ViewRootImpl,相关执行流程可以参考Android View 显示原理分析,看一下setView()方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......
......
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
......
......
if (mInputChannel != null) {
........
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}
......
}
当窗口端通过addWindow()函数获取InputChannel后,便会使用它创建一个InputEventReceiver对象。InputEventReceiver对象可以接收来自InputChannel的输入事件,并触发其onInputEvent()回调,看一下WindowInputEventReceiver的实现:
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
.......
}
WindowInputEventReceiver继承InputEventReceiver,在创建对象时调用了父类的构造方法,接下来看一下InputEventReceiver:
private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
InputChannel inputChannel, MessageQueue messageQueue);
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
........
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
可以看到,在构造方法内部会执行nativeInit(),将inputChannel及messageQueue传入,返回NativeInputEventReceiver对象引用mReceivePtr,后续在事件处理完后调用finishInputEvent()发送完成消息时会用到,nativeInit()是native方法,会调用到native层,对应的实现代码路径为: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);
......
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
......
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
......
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
创建了NativeInputEventReceiver对象携带inputChannel,然后执行initialize()方法,先看一下NativeInputEventReceiver构造方法:
class NativeInputEventReceiver : public LooperCallback {
.......
}
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) {
}
NativeInputEventReceiver的构造函数很简单,它保存了java层InputEventReceiver对象的引用,并创建了一个InputConsumer类型的对象对InputChannel进行封装。
InputConsumer与InputPublisher一样,它也封装了InputChannel,负责对其进行写入和读取操作,同时也负责InputMessage的封装与解析。不过它们的功能正好相反,InputConsumer接收的是输入事件,发送的则是反馈。
接下来看一下initialize()方法:
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) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
在initialize()内部执行了setFdEvents(),然后在setFdEvents()内部获取到InputChannel的fd,然后通过Looper->addFd()对InputChannel的可读性事件进行监听,此时就已经连接到窗口了;
用一张流程图总结一下连接窗口过程:
连接到窗口.png
当有InputMessage可读时,NativeInputEventReceiver的handleEvent()函数会被Looper调用,此时便可以通过InputConsumer从InputChannel中读取事件,然后回调到java层的onInputEvent()函数,在java层完成事件的处理后,便可通过InputConsumer发送处理完毕的反馈给InputDispatcher。
接下来看一下handleEvent()是如何将事件传给java层的onInputEvent()方法:
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
.......
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
.....
}
在handleEvent()内部会调用到consumeEvents():
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
......
for (;;) {
......
InputEvent* inputEvent;
int32_t displayId;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent, &displayId,
&motionEventType, &touchMoveNum, &flag);
.......
case AINPUT_EVENT_TYPE_MOTION: {
MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
if (inputEventObj) {
.....
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
displayId);
.......
env->DeleteLocalRef(inputEventObj);
}
......
}
在consumeEvents()内部主要做了三项工作:
1.调用InputConsumer的consume()从InputChannel中读取一条InputMessage,解析为InputEvent后通过inputEvent参数传出;InputConsumer的实现也是在InputTransport.cpp中,跟InputPublisher对应;
2.根据事件的类型分别创建KeyEvent与MotionEvent类型的java对象;
3.通过JNI回调java层的InputEventReceiver的dispatchInputEvent()函数;
4.InputConsumer
前面第一项工作中涉及到InputConsumer,该类的实现路径为:frameworks/native/libs/input/InputTransport.cpp,该类是和InputPublisher对应的,一个来发送,一个来消费处理,处理完毕后发送finish signal;
接着上面进行分析,再看一下Java层的处理:
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event, displayId);
}
dispatchInputEvent函数首先在字典中保存来自InputDispatcher的事件序列号,以满足发送反馈之需。之后便调用onInputEvent()函数,交由子类进行输入事件的实际处理工作。onInputEvent()函数可由使用者重写,从而实现各种各样的工作,Android控件根ViewRootImpl在收到事件后会将其派发给特定的控件。
5.简单总结
1.事件发送主要是通过InputChannel来完成;
2.在wms 执行addView()时,调用openInputChannel来从native层获取inputchannels数组,一个通过ims registerInputChannel来连接InputDispatcher,另外一个通过InputEventReceiver来连接窗口;
3.InputDispatcher经过Connection最终通过InputPublisher将事件发送到目标窗口;
4.NativeInputEventListener监听到事件到来时通过InputConsumer处理InputMessage后回调Java层接口;
到这里事件就已经发送到对应的窗口了,接下来看一下事件的处理Android IMS原理解析之processEvent