SwallowJoe的博客

Be a real go-getter,
NEVER SETTLE!

0%

WMS(7)-窗口和InputChannel的联系

以下分析基于Android S.

简述

本文集中研究input和窗口的关系, 特别是input系统是如何将事件传给正确的窗口进程的。

在Activity的resume过程中,会通过ViewRootImpl.setView向WMS传递其窗口信息,我们还是从这里入手:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
......
requestLayout();
// 创建InputChannel
InputChannel inputChannel = null;
// INPUT_FEATURE_NO_INPUT_CHANNEL 的意思是该窗口不接受input事件
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
// 仅仅是实例化InputChannel, 啥也没有做
inputChannel = new InputChannel();
}
.......
// 将InputChannel传入WMS中, 我们先进入看这个
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);
......
// 处理后续
if (inputChannel != null) {
......
// [3.1] 创建InputEventReceiver用于接收Input事件
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
......
}
......
}

// WMS.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {

......
// 初始化WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
......
// attrs就是上面ViewRootImpl中的mWindowAttributes
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
// [1.1] 初始化InputChanel
win.openInputChannel(outInputChannel);
}
......
}

注意初始化InputChannel是在WindowState之后,也是通过其openInputChannel的。

一. InputChannel的初始化和Dispose

1.1 WindowState.openInputChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
// 获取名称: 数字+窗口包名
String name = getName();
// [1.2] 通过InputManager创建InputChannel
mInputChannel = mWmService.mInputManager.createInputChannel(name);
// 注意这里的token, 后面会发现其实是native创建的BBinder
mInputChannelToken = mInputChannel.getToken();
// mInputWindowHandle是WindowState初始化时创建的
mInputWindowHandle.setToken(mInputChannelToken);
// 将该InputChannel和WindowState作为KV对保存
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) {
// [2.1] 将创建的InputChannel拷贝到outInputChannel中, 这个outInputChannel就是应用进程中创建的InputChannel
mInputChannel.copyTo(outInputChannel);
} else {
// If the window died visible, we setup a fake input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create fake event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
}
}

1.2 InputManagerService.createInputChannel

1
2
3
4
5
private static native InputChannel nativeCreateInputChannel(long ptr, String name);

public InputChannel createInputChannel(String name) {
return nativeCreateInputChannel(mPtr, name);
}

通过JNI由native层创建InputChannel。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// com_android_server_input_InputManagerService.cpp
static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
// [1.3] 创建Native层的InputChannel
base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(env, name);

if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
message += StringPrintf(" Status=%d", inputChannel.error().code());
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}

// [1.4] 通过JNI调用java层函数,创建java对象InputChannel
jobject inputChannelObj =
android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
if (!inputChannelObj) {
return nullptr;
}

// [1.5] 注册资源回收处理的回调函数
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
return inputChannelObj;
}

1.3 NativeInputManager.createInputChannel

1
2
3
4
5
6
7
8
9
10
11
12
// com_android_server_input_InputManagerService.cpp
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
// [1.3.1] 交给InputDispatcher
return mInputManager->getDispatcher()->createInputChannel(name);
}

// InputManager.cpp
sp<InputDispatcherInterface> InputManager::getDispatcher() {
return mDispatcher;
}

关于InputDispatcher的初始化后续单独分析。

1.3.1 InputDispatcher.createInputChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// InputTransport.cpp
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {

std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
// [1.3.2] InputChannel是成对存在的
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

if (result) {
return base::Error(result) << "Failed to open input channel pair with name " << name;
}

{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
// 创建Connection, 用于记录此次连接行为
sp<Connection> connection =
new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);

if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
ALOGE("Created a new connection, but the token %p is already known", token.get());
}
// 将此次连接行为保存在mConnectionsByToken中
mConnectionsByToken.emplace(token, connection);
// 利用bind绑定回调函数,顺便固定回调时的第二个参数为token
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
// 将文件描述符添加进Looper, 这样有事件时会回调handleReceiveCallback
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock

// 唤醒Looper以执行
mLooper->wake();
// 返回客户端InputChannel
return clientChannel;
}

创建Native层的InputChannel其实就是利用socket创建了一对套接字,分别作为服务端和客户端,并将服务端的token作为key,存入作为记录此次InputChannel的Connection至mConnectionsByToken中。其后利用bind绑定handleReceiveCallback作为回调函数(注意这里固定了其第二个参数为当前的token),然后将服务端的文件描述符存入mLooper中。最后唤醒mLooper。

注意这里的mLooper是InputDispatcher初始化时创建的:

mLooper = new Looper(false);

所以这里InputChannel中的服务端就是用来分发input事件的,而客户端的InputChannel应该就需要传给对应的应用进程了。

1.3.2 InputChannel.openInputChannelPair

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
status_t InputChannel::openInputChannelPair(const std::string& name,
std::unique_ptr<InputChannel>& outServerChannel,
std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
// 原来InputChannel之所以需要成对是因为这里是通过socket实现的
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%s(%d)", name.c_str(),
strerror(errno), errno);
outServerChannel.reset();
outClientChannel.reset();
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]);
// [1.3.3] 创建native层的InputChannel对象
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;
}

socketpair()函数用于创建一对无名的、相互连接的套接字。如果函数成功,则返回0,创建好的套接字分别是sockets[0]和sockets[1];否则返回-1,错误码保存于errno中。

  1. 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sockets[0]中写,从sockets[1]中读;或者从sockets[1]中写,从sockets[0]中读; 
  2. 如果往一个套接字(如sockets[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sockets[1])上读成功; 
  3. 读、写操作可以位于同一个进程,也可以分别位于不同的进程。因为sockets[0]和sockets[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。

1.3.3 InputChannel.create

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
// using 'new' to access a non-public constructor
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)) {
if (DEBUG_CHANNEL_LIFECYCLE) {
ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}

native层的InputChannel原来就是一对套接字,分成server端和client端,用于跨进程通信。

1.4 android_view_InputChannel_createJavaObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// android_view_InputChannel.cpp#123
jobject android_view_InputChannel_createJavaObject(JNIEnv* env,
std::unique_ptr<InputChannel> inputChannel) {
std::string name = inputChannel->getName();
// 创建NativeInputChannel
jlong ptr = android_view_InputChannel_createInputChannel(env, std::move(inputChannel));
// 初始化java层的InputChannel
// gInputChannelClassInfo.mCtor => "<init>";
// 调用java层InputChannel的构造函数,生成对象
jobject javaInputChannel =
env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.mCtor);
if (!javaInputChannel) {
ALOGE("Failed to create a Java InputChannel for channel %s.", name.c_str());
return nullptr;
}
// gInputChannelClassInfo.mSetNativeInputChannel => "setNativeInputChannel"
// [1.4.1] 调用setNativeInputChannel函数设置ptr
env->CallVoidMethod(javaInputChannel, gInputChannelClassInfo.mSetNativeInputChannel, ptr);
if (env->ExceptionOccurred()) {
ALOGE("Failed to set native ptr to the Java InputChannel for channel %s.",
inputChannel->getName().c_str());
return nullptr;
}
// 返回java层的InputChannel对象
return javaInputChannel;
}

static jlong android_view_InputChannel_createInputChannel(
JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
std::make_unique<NativeInputChannel>(std::move(inputChannel));

return reinterpret_cast<jlong>(nativeInputChannel.release());
}

NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
: mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}

经过1.3我们知道这里传入的InputChannel其实是一对socket中代表客户端的那个, 先通过std::move将InputChannel存入新创建的NativeInputChannel对象中,然后通过JNI构造出java层的InputChannel,在调用其setNativeInputChannel,将NativeInputChannel保存在其mPtr中。

1.4.1 InputChannel.setNativeInputChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
private void setNativeInputChannel(long nativeChannel) {
if (nativeChannel == 0) {
throw new IllegalArgumentException("Attempting to set native input channel to null.");
}
if (mPtr != 0) {
throw new IllegalArgumentException("Already has native input channel.");
}
if (DEBUG) {
Slog.d(TAG, "setNativeInputChannel : " + String.format("%x", nativeChannel));
}
sRegistry.registerNativeAllocation(this, nativeChannel);
mPtr = nativeChannel;
}

所以java层的InputChannel里的mPtr是对应NativeInputChannel对象的。

1.5 android_view_InputChannel_setDisposeCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// android_view_InputChannel.cpp
void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data) {
// 通过java层InputChannel中存储的mPtr找到对应的NativeInputChannel
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
} else {
// [1.5.1] 这里的data就是NativeInputManager
nativeInputChannel->setDisposeCallback(callback, data);
}
}

static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobject inputChannelObj) {
jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast<NativeInputChannel*>(longPtr);
}

1.5.1 NativeInputChannel.setDisposeCallback

1
2
3
4
void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
mDisposeCallback = callback;
mDisposeData = data;
}

将回调函数和相关参数保存起来, 当进程死亡,或者窗口被销毁时,会主动调用InputChannel.dispose()回收资源,最后就会调用到NativeInputChannel.dispose了。

1.5.2 NativeInputChannel.dispose

1
2
3
4
5
6
7
8
9
10
11
12
13
void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
if (!mInputChannel) {
return;
}

if (mDisposeCallback) {
// [1.5.3] 调用回调函数handleInputChannelDisposed
mDisposeCallback(env, obj, mInputChannel, mDisposeData);
mDisposeCallback = nullptr;
mDisposeData = nullptr;
}
mInputChannel.reset();
}

1.5.3 handleInputChannelDisposed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
const std::shared_ptr<InputChannel>& inputChannel,
void* data) {
NativeInputManager* im = static_cast<NativeInputManager*>(data);

ALOGW("Input channel object '%s' was disposed without first being removed with "
"the input manager!",
inputChannel->getName().c_str());
// 移除连接
im->removeInputChannel(env, inputChannel->getConnectionToken());
}

status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */,
const sp<IBinder>& connectionToken) {
ATRACE_CALL();
return mInputManager->getDispatcher()->removeInputChannel(connectionToken);
}

1.5.4 InputDispatcher.removeInputChannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);

status_t status = removeInputChannelLocked(connectionToken, false /*notify*/);
if (status) {
return status;
}
} // release lock

mLooper->wake();
return OK;
}

status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
bool notify) {
// 通过token找到对应的Connection
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
// Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
return BAD_VALUE;
}
// [1.5.4.1] 将该Connection从集合中移除
removeConnectionLocked(connection);

if (connection->monitor) {
removeMonitorChannelLocked(connectionToken);
}
// 将对应服务端Socket描述符从mLooper中移除,这样就不会接收到事件了
mLooper->removeFd(connection->inputChannel->getFd());

nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
// 标记该Connection为ZOMBIE状态
connection->status = Connection::STATUS_ZOMBIE;
return OK;
}

移除InputChannel的工作也不多,将Connection从直接保存的集合中移除,顺便将其中保存的服务端Socket描述符从mLooper中移除即可。最后标记被移除的Connection状态为ZOMBIE状态。

1.5.4.1 InputDispatcher.removeConnectionLocked

1
2
3
4
5
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
// 将该连接从mConnectionsByToken中移除
mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}

直接将该Connection从mConnectionsByToken和mAnrTracker中移除即可。

二. 应用进程InputChannel的来源

通过 InputChannel的初始化 我们知道InputChannel的Native实现就是一对Socket,其中服务端作为input事件分发者被加入InputDispatcher的Looper中,客户端会被存入NativeInputChannel中,而后通过JNI存入java层的InputChannel的mPtr中,但是这里java层的InputChannel还是处于SystemServer进程,还没有看到应用进程InputChannel是如何被赋值的。

接下来我们先研究下InputChannel的拷贝。

2.1 InputChannel.copyTo

1
2
3
4
5
6
7
8
9
10
11
12
13
public void copyTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
if (outParameter.mPtr != 0) {
throw new IllegalArgumentException("Other object already has a native input channel.");
}
// 2.2 nativeDup
// setNativeInputChannel我们已经分析过了就不在赘述,重点看nativeDup函数
outParameter.setNativeInputChannel(nativeDup(mPtr));
}

private native long nativeDup(long channel);

这里思考一下为什么不直接将mPtr赋值给outParameter的mPtr呢?

2.2 android_view_InputChannel_nativeDup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// android_view_InputChannel.cpp#123
static jlong android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jlong channel) {
NativeInputChannel* nativeInputChannel =
reinterpret_cast<NativeInputChannel*>(channel);

if (nativeInputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
return 0;
}

std::shared_ptr<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
return 0;
}
// 2.3 调用native层的InputChannel.dup函数
std::unique_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
jniThrowRuntimeException(env, message.c_str());
}
// 创建新的NativeInputChannel对象
return reinterpret_cast<jlong>(new NativeInputChannel(std::move(dupInputChannel)));
}

android_view_InputChannel_nativeDup的作用就是根据传入的java层InputChannel创建一个新的NativeInputChannel作为客户端socket, 与服务端Socket的InputChannel对应。

2.3 InputChannel.dup

1
2
3
4
5
6
std::unique_ptr<InputChannel> InputChannel::dup() const {
// 2.3.1 dupFd创建一个新的文件描述符,但是执行同一个文件
base::unique_fd newFd(dupFd());
// 根据新的文件描述符创建新的InputChannel对象
return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}

2.3.1 InputChannel.dupFd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
base::unique_fd InputChannel::dupFd() const {
// getFp返回的就是该InputChannel的mFd
// ::dup 的作用是复制文件描述符,使多个文件描述符指向同一个文件
android::base::unique_fd newFd(::dup(getFd()));
if (!newFd.ok()) {
ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
strerror(errno));
const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
// If this process is out of file descriptors, then throwing that might end up exploding
// on the other side of a binder call, which isn't really helpful.
// Better to just crash here and hope that the FD leak is slow.
// Other failures could be client errors, so we still propagate those back to the caller.
LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
getName().c_str());
return {};
}
return newFd;
}

dupFd是通过 ::dup(int oldFd) 函数复制文件描述符,使新的文件描述符指向参数描述符指向的同一个文件。

2.4 Binder通信中AIDL里的out标记

看完了InputChannel的copyTo函数,我们知道该函数就是在native层创建了新的NativeInputChannel,但是还是和之前客户端InputChannel一样指向同一个Socket描述符,这样服务端Socket发的消息会同步传给信息的InputChannel了。

但是这里我们还是没有看到应用进程的InputChannel是如何被赋值的,在WMS.addWindow函数中,outInputChannel在被mInputChannel.copyTo函数赋值后,就没有继续使用过了。这里我们就要看到应用进程和WMS通信的桥梁Session的AIDL文件定义了:

1
2
3
4
5
6
7
8
9
10
11
// IWindowSession.aidl
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
out InputChannel outInputChannel, out InsetsState insetsState,
out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
in InsetsState requestedVisibility, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out InsetsState insetsState);

毫无意外,所有的InputChannel都是被标记了out标签,这样binder回调时会将该参数回传给调用进程,即应用进程。 这样一来一个WindowState对应一对Socket,分成服务端和客户端,服务端Socket的描述符被存入InputDispatcher的mConnectionsByToken,也被添加到其mLooper中用于转发Input事件至客户端Socket中。而客户端Socket又被dup函数复制成两份NativeInputChannel, 一份存在WindowState的mInputChannel中,另一份通过Binder调用存入应用进程的ViewRootImpl的WindowInputEventReceiver对象中。

三. 应用进程注册接听Input事件

在ViewRootImpl.setView函数中,当应用进程通过binder调用addToDisplayAsUser获得被复制的InputChannel后,会通过该InputChannel创建WindowInputEventReceiver。

3.1 WindowInputEventReceiver初始化

1
2
3
4
5
6
7
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}

......
}

WindowInputEventReceiver是继承了InputEventReceiver的,这里的构造函数也仅仅是将传入的参数继续调用给父类的构造函数。

3.2 InputEventReceiver初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
// 3.3 通过JNI创建native层的对象
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);

mCloseGuard.open("dispose");
}

private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
InputChannel inputChannel, MessageQueue messageQueue);

3.3 android_view_InputEventReceiver.cpp:nativeInit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// android_view_InputEventReceiver.cpp#486
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
// 通过java层的InputChannel转成Native层的InputChannel
std::shared_ptr<InputChannel> inputChannel =
android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
// 通过java层的MessageQueue转成Native层的MessageQueue
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
// 3.4 创建NativeInputEventReceiver
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
// 3.5 调用初始化
status_t status = receiver->initialize();
if (status) {
std::string message =
android::base::StringPrintf("Failed to initialize input event receiver. status=%d",
status);
jniThrowRuntimeException(env, message.c_str());
return 0;
}

receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}

3.4 NativeInputEventReceiver初始化

1
2
3
4
5
6
7
8
9
10
11
12
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) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
}
}

初始化NativeInputEventReceiver

3.5 NativeInputEventReceiver.initialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
status_t NativeInputEventReceiver::initialize() {
// ALOOPER_EVENT_INPUT = 1 << 0;
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
// 此时mFdEvents还只是0
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
// 将InputChannel中的客户端Socket的文件描述符加入的Looper中监听
mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}

这里设置文件描述符,将作为客户端InputChannel的Socket的描述符添加到该应用进程的主线程Looper中(ViewRootImpl.setView是主线程才能被调用的)。

四. 小结

WindowState和InputChannel的相关流程图如下:

7-1

InputChannel的Native实现就是一对Socket,其中服务端作为input事件分发者被加入InputDispatcher的Looper中,客户端会被存入NativeInputChannel中。一个WindowState对应一对Socket,分成服务端和客户端,服务端Socket的描述符被存入InputDispatcher的mConnectionsByToken,也被添加到其mLooper中用于转发Input事件至客户端Socket中。而客户端Socket又被dup函数复制成两份NativeInputChannel, 一份存在WindowState的mInputChannel中,另一份通过Binder调用存入应用进程的ViewRootImpl的WindowInputEventReceiver对象中。

接下来我们看看input事件是如何传给正确的窗口进程的。

欢迎关注我的其它发布渠道