Android InputChannel事件发送接收系统分析
本文基于Android12。
InputChannel表示其他进程通过文件描述符传递输入事件到View的通道,因为需要跨进程传输,实现了Parcelable序列化接口,所以也能够理解Java层的InputChannel后面为什么使用copyTo()方法初始化。
输入事件的接收方是View,所以InputChannel的创建肯定和View的创建流程有关,关于View的创建流程参考:https://blog.csdn.net/qq_36063677/article/details/129908973。
一、InputChannel初始化
ViewRootImpl在setView()方法实例化了Java层的InputChannle对象,但是正如ViewRootImpl创建的mSurface对象一样,这只是一个引用,一个“空的”对象,后续在WindowManagerService经过实际的初始化,再通过copyTo()方法拷贝到InputChannel引用中。
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
// 1.创建 inputChannel引用。
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
// 2.传递引用给mWindowSession,inputChannel在WMS中被初始化,并通过copyTo()全拷贝到inputChannel引用。
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls, attachedFrame, sizeCompatScale);
// 3.通过inputChannel创建WindowInputEventReceiver对象,接收处理输入事件。ALOOPER_EVENT_INPUT
if (inputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
}
}
setView()方法中关于InputChannel的操作主要有3步:
- 创建 inputChannel引用。就是实例化InputChannel引用,InputChannel构造方法是个空方法,所以什么实际操作都没有做。
- 传递引用给mWindowSession,inputChannel在WMS中被初始化,并通过copyTo()全拷贝到inputChannel引用。
- 通过inputChannel创建WindowInputEventReceiver对象,接收处理输入事件。
我们从第2步开始分析。
1.1 openInputChannle
mWindowSession将inputChannel引用传递给WMS.addWindow()方法:
// WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Rect outAttachedFrame,
float[] outSizeCompatScale) {
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
}
addWindow()创建了WindowState对象,调用其openInputChannel(outInputChannel)方法。
// WindowState.java
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
String name = getName();
mInputChannel = mWmService.mInputManager.createInputChannel(name);
if (outInputChannel != null) {
mInputChannel.copyTo(outInputChannel);
}
}
}
WindowState通过InputMangerService创建InputChannel,经过NativeInputManagerService类的native方法createInputChannel(name),最终到InputDispatcher::createInputChannel(name)方法实际创建。
1.1.1 InputDispatcher::createInputChannel
// InputDispatcher.h
// All registered connections mapped by input channel token.
std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
GUARDED_BY(mLock);
// InputDispatcher.cpp
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
// 1.通过CPP层的InputChannel创建serverChannel,clientChannel
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
{
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
// 2.得到serverChannel的文件描述符 fd
int fd = serverChannel->getFd();
// 3.将connection对象添加到 mConnectionsByToken管理
sp<Connection> connection =
new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
mConnectionsByToken.emplace(token, connection);
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid);
// 4.监听serverChannel的文件描述符 fd,当有事件发生时,回调callback
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
}
// 5.唤醒mLooper
mLooper->wake();
return clientChannel;
}
InputDispatcher主要做了5件事:
- 通过CPP层的InputChannel创建serverChannel,clientChannel,最后返回clientChannel给Java层的InputChannel,到这里Java层InputChannel才被初始化完成,返回创建WindowInputEventReceiver对象。
- 得到serverChannel的文件描述符 fd。
- 将connection对象添加到 mConnectionsByToken管理,mConnectionsByToken定义在InputDispatcher.h文件,管理所有连接的InputChannel对象,map的key是token,又是使用Binder对象作为token。inputflinger文章中(https://blog.csdn.net/qq_36063677/article/details/130475299)4.3.3小节InputDispatcher在分发事件时就是通过这个mConnectionsByToken获取到具体的connection,发送事件。
- 监听serverChannel的文件描述符 fd,ALOOPER_EVENT_INPUT表示为要监听的文件类型,当有事件发生时,回调callback方法handleEvent(),也就是InputDispatcher::handleReceiveCallback()。LooperEventCallback继承了LooperCallback类,LooperCallback是Looper监听文件描述符回调方法的标准类,当文件描述符fd上有事件到来时,LooperCallback的handleEvent()方法会被执行,关于Looper->addFd()更多细节参考:https://blog.csdn.net/chwan_gogogo/article/details/46953563
- 唤醒mLooper,mLooper在InputDispatcher构造方法中被初始化,mLooper = new Looper(false);。
查看cpp层InputChannel::openInputChannelPair()具体细节:
1.1.2 InputChannel::openInputChannelPair
// InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
std::unique_ptr<InputChannel>& outServerChannel,
std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
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));
sp<IBinder> token = new BBinder();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,
android::base::unique_fd fd, sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
}
InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
: mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
}
- InputChannel使用socket通信,openInputChannelPair()方法创建两个socke对象,一个client,一个server。socketpair()函数用于创建一对无名的、相互连接的套接子。如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1],所以outServerChannel和outClientChannel这两个socket在创建的时候就是相互连接的,之后只需要在各自的fd中通过send()发送数据就好。
- setsockopt()设置socket属性,在<sys/socket.h>头文件中声明,实例化一个BBinder()对象作为token使用,这里outServerChannel和outClientChannel都使用的是同一个token。
- InputChannel::create()通过fcntl()设置socket为非阻塞类型,fcntl()方法可以改变已打开的文件性质。
- 封装InputChannel对象。
InputChannel初始化过程终于结束了,那么1.1.1小节中mLooper监听的serverChannel的文件描述符 fd什么时候会触发呢?
答案在上一篇inputflinger文章中(https://blog.csdn.net/qq_36063677/article/details/130475299)4.3.3小节。
二、InputChannel发送事件
InputDispatcher在接收到事件后,InputDispatcher::dispatchEventLocked()从mConnectionsByToken变量中通过token获取到Connection对象,最终在4.3.4.2 小节startDispatchCycleLocked()方法调用
connection->inputPublisher.publishMotionEvent()发送输入事件,这里还是以Motion事件为例:
// InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
float yPrecision, float xCursorPosition, float yCursorPosition,
const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputMessage msg;
msg.header.type = InputMessage::Type::MOTION;
msg.header.seq = seq;
msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
//.......
return mChannel->sendMessage(&msg);
}
mChannel是之前创建的InputChannel对象serverChannel,查看sendMessage(&msg)方法:
// 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(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
return OK;
}
调用socket的send()函数发送数据。
至此输入事件通过socket发送出去了,InputDispatcher执行回调LooperEventCallback,那么事件又是如何被接收的呢?
三、InputEventReceiver
ViewRootImpl在setView()方法实例化InputChannel后,作为参数初始化WindowInputEventReceiver对象,WindowInputEventReceiver继承InputEventReceiver类。
3.1 WindowInputEventReceiver
WindowInputEventReceiver也在ViewRootImpl中定义:
// ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
List<InputEvent> processedEvents;
try {
processedEvents =
mInputCompatProcessor.processInputEventForCompatibility(event);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (processedEvents != null) {
if (processedEvents.isEmpty()) {
// InputEvent consumed by mInputCompatProcessor
finishInputEvent(event, true);
} else {
for (int i = 0; i < processedEvents.size(); i++) {
enqueueInputEvent(
processedEvents.get(i), this,
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
}
}
} else {
enqueueInputEvent(event, this, 0, true);
}
}
}
// InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
mInputChannel, mMessageQueue);
mCloseGuard.open("InputEventReceiver.dispose");
}
WindowInputEventReceiver构造方法中将参数传递给了父类InputEventReceiver,实现了onInput()方法,后续接收到事件后通过这个方法处理Java层的分发逻辑,Eventlooper是ViewRoolImpl的当前线程Looper.myLooper(),也就是主线程,InputEventReceiver调用nativeInit()继续下一步操作,创建NativeInputEventReceiver。
3.2 NativeInputEventReceiver
NativeInputEventReceiver定义在JNI文件中,方便后续回调JAVA方法。
// android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
// 获取InputChannel
std::shared_ptr<InputChannel> inputChannel =
android_view_InputChannel_getInputChannel(env, inputChannelObj);
// 获取messageQueue
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
// 1. 实例化NativeInputEventReceiver
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
// 2. receiver->initialize()
status_t status = receiver->initialize();
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get())
}
nativeInit()主要做了两件事,实例化NativeInputEventReceiver,并且调用其initialize()方法。
3.2.1 NativeInputEventReceiver实例化
// android_view_InputEventReceiver.cpp
NativeInputEventReceiver::NativeInputEventReceiver(
JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue)
: mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel),
mMessageQueue(messageQueue),
mBatchedInputEventPending(false),
mFdEvents(0) {
}
NativeInputEventReceiver构造方法中持有Java层InputEventRecevier对象的引用mReceiverWeakGlobal, 将inputChannel作为参数实例化mInputConsumer对象。
InputConsumer类定义在InputTransport.h头文件,从InputChannel消费事件。
// InputTransport.cpp
InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
: mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
client端的inputChannel被赋值给InputConsumer的mChannel变量。
回到NativeInputEventReceiver初始化流程中。
3.2.2 NativeInputEventReceiver::initialize
// android_view_InputEventReceiver.cpp
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, nullptr);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
initialize()监听client InputChannel的文件描述符 fd,文件类型文件类型和发送端一样,也是ALOOPER_EVENT_INPUT。在上述1.1.1小节中我们知道后续会回调LooperCallback的handleEvent()方法,NativeInputEventReceiver也继承了LooperCallback类,实现了自己的handleEvent():
3.3 handleEvent
// android_view_InputEventReceiver.cpp
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, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
}
if (events & ALOOPER_EVENT_OUTPUT) {
const status_t status = processOutboundEvents();
if (status == OK || status == WOULD_BLOCK) {
return KEEP_CALLBACK;
} else {
return REMOVE_CALLBACK;
}
}
return KEEP_CALLBACK;
}
handleEvent只是对事件类型进行区分,如果是ALOOPER_EVENT_INPUT类型,详细具体到处理流程在consumeEvents()方法。
3.4 consumeEvents
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
for (;;) {
uint32_t seq;
// 1.InputConsumer从socket读取封装事件
InputEvent* inputEvent;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
// 2.区分事件类型,封装为Java层的InputEvent对象
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
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;
}
}
// 3.调用Java层InputEventReceiver对象的dispatchInputEvent方法,参数为seq,inputEventObj
if (inputEventObj) {
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
env->DeleteLocalRef(inputEventObj);
}
}
}
consumeEvents()处理了事件接收的主要流程:
- 启动一个死循环,不断从InputConsumer获取事件,赋值到inputEvent引用,InputConsumer持有客户端InputChannel的引用,从客户端socket读取数据,将数据解析封装成InputEvent对象。
- 将InputEvent类型进行区分,具体共有AINPUT_EVENT_TYPE_KEY,AINPUT_EVENT_TYPE_MOTION,AINPUT_EVENT_TYPE_FOCUS,AINPUT_EVENT_TYPE_CAPTURE,AINPUT_EVENT_TYPE_DRAG,AINPUT_EVENT_TYPE_TOUCH_MODE六种类型,对于Motion事件,封装成Java层的MotionEvent对象。
- receiverObj指向NativeInputEventReceiver实例化时传递过来的Java层InputEventReceiver引用,调用Java层InputEventReceiver对象的dispatchInputEvent方法,参数为seq,inputEventObj,至此回到了Java层的处理流程。
在分析Java层的处理流程之前,先看下InputConsumer是如何读取socket数据解析成InputEvent对象的。
3.4.1 InputConsumer.consume
// InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
while (!*outEvent) {
status_t result = mChannel->receiveMessage(&mMsg);
switch (mMsgmMsg.header.type) {
case InputMessage::Type::KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.header.seq;
*outEvent = keyEvent;
break;
}
case InputMessage::Type::MOTION: {
MotionEvent* motionEvent = factory->createMotionEvent();
if (!motionEvent) return NO_MEMORY;
updateTouchState(mMsg);
initializeMotionEvent(motionEvent, &mMsg);
*outSeq = mMsg.header.seq;
*outEvent = motionEvent;
break;
}
}
}
return OK;
}
mChannel是NativeInputEventReceiver传递过去的客户端InputChannel对象,InputConsumer从mChannel读取消息,在上述第二小节:InputChannel发送事件 可知,发送的InputMessage对象封装了mMsg.header.type等事件类型消息,这里通过事件类型消息进行区分,封装成具体对应的输入事件,如KeyEvent,MotionEvent等,赋值给outEvent。
InputChannel通过recv()方法接收socket消息:
// InputTransport.cpp
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
}
3.4.2 dispatchInputEvent
事件的读取解析流程到此结束了,接下来看看InputReceiver是如何分发事件的。
// InputEventReceiver.java
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
InputEventReceiver调用onInputEvent(event)开始处理事件的分发流程,上述3.1小节可知,onInputEvent方法在WindowInputEventReceiver类中被覆盖。Java层的事件分发流程参考:https://editor.csdn.net/md/?articleId=130476234