输入事件最终还是要发送给窗口去处理的,那么我们就看看在ViewRootImpl在添加窗口的时候做了什么操作。
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
......
try {
......
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);
......
if (inputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
......
}
}
1)、创建一个空的InputChannel对象,跨进程调用WindowManagerService.addWindow方法将这个inputChannel对象传给WindowManagerService进行初始化。
2)、如果从WMS返回的inputChannel不为空,则基于这个inputChannel生成一个WindowInputEventReceiver对象mInputEventReceiver。
接下来分别详细分析以上两部分的具体内容。
一、InputChannel的初始化
1 Session.addToDisplayAsUser
ViewRootImpl持有的mWindowSession是一个IWindowSession类型的对象,它在服务端的实现是Session,因此这里调用的是Session的addToDisplayAsUser方法。
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
这里又调用了WindowManagerService的addWindow方法。
2 WindowManagerService.addWindow
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
......
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
......
}
如果当前窗口设置了INPUT_FEATURE_NO_INPUT_CHANNEL,代表当前窗口不希望接收输入事件;否则调用WindowState#openInputChannel。
3 WindowState.openInputChannel
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputChannelToken = mInputChannel.getToken();
mInputWindowHandle.setToken(mInputChannelToken);
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) {
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);
}
}
调用InputManagerService#createInputChannel生成InputChannel对象并且初始化。
4 InputManagerService.createInputChannel
/**
* Creates an input channel to be used as an input event target.
*
* @param name The name of this input channel
*/
public InputChannel createInputChannel(String name) {
return nativeCreateInputChannel(mPtr, name);
}
这个方法用来创建一个可以作为输入事件发送目标的InputChannel。
这里调用了JNI层的InputManagerService.nativeCreateInputChannel。
5 com_android_server_input_InputManagerService.nativeCreateInputChannel
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();
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;
}
jobject inputChannelObj =
android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
if (!inputChannelObj) {
return nullptr;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
return inputChannelObj;
}
1)、调用createInputChannel创建C++层InputChannel对象。
2)、基于生成的C++层inputChannel对象生成Java层InputChannel对象inputChannelObj并返回。
继续追踪C++层InputChannel的创建流程。
6 NativeInputManager.createInputChannel
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
return mInputManager->getDispatcher()->createInputChannel(name);
}
调用C++层的InputManager的InputDispatcher.createInputChannel函数。
7 InputDispatcher.createInputChannel
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
#if DEBUG_CHANNEL_CREATION
ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
// 7.1 生成InputChannel对
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
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);
// 7.2 创建Connection对象
const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
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.emplace(token, connection);
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return clientChannel;
}
该函数做了两方面工作:
1)、调用InputChannel.openInputChannelPair生成一对InputChannel:服务端serverChannel和客户端clientChannel。
2)、基于服务端serverChannel生成一个Connection对象,然后返回客户端clientChannel。
7.1 生成InputChannel对
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
调用InputChannel.openInputChannelPair生成一对InputChannel。
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)) {
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]);
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;
}
这里生成了一个socket数组:
int sockets[2];
有必要先对socket做一个简单的介绍:
socket是通信端点的抽象,一个socket构成一个连接的一端,而一个连接可完全由一对socket规定。
Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
再来看InputChannel.openInputChannelPair到底做了什么工作:
1)、调用socketpair()函数创建一对无名的、相互连接的套接字。 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。
2)、setsockopt()函数用于任意类型、任意状态套接口的设置选项值。这里为这一对套接字都设置了两个缓冲区(接收缓冲区SO_RCVBUF和发送缓冲区SO_SNDBUF)。
3)、创建一个IBinder类型的token,这个token由这一对InputChannel,可以作为这一对InputChannel在Native层的标识。
4)、为这一对套接字创建两个fd,serverFd和clientFd,那么当服务端进程通过serverFd把数据写到sockets[0] 的发送缓冲区的时候,sockets[0] 就会把发送缓冲区里面的数据写到sockets[1] 的接收缓冲区里面,客户端进程就可以读取clientFd得到那些数据了,相反同理。
5)、再基于以上的IBinder对象和socket描述符生成最终的serverInputChannel和clientInputChannel。
现在生成了持有一对socket文件描述符的InputChannel对,那么在需要通信的时候就可以通过InputChannel拿到socket文件描述符然后调用socket的相关函数来发送数据,“channel”一词本身也有通道,管道的含义,因此C++层的InputChannel可以看作是对socket做的一层封装。
7.2 创建Connection对象
{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
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.emplace(token, connection);
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return clientChannel;
1)、基于服务端InputChannel生成一个Connection对象,Connection用来管理与之相关的InputChannel的分发状态,然后InputDispatcher将键值对<token, connection>存放到mConnectionsByToken中,token是在上一步生成服务端InputChannel的时候创建的,mConnectionsByToken的定义是:
// All registered connections mapped by input channel token.
std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
GUARDED_BY(mLock);
那么后续InputDispatcher就可以通过token获取到Connection,进而拿到InputChannel对象。
另外这里看到并没有为客户端InputChannel生成一个Connection对象,那么就说明InputDispatcher.mConnectionsByToken只保存服务端InputChannel对应的Connection。
2)、生成一个LooperEventCallback,回调触发的时候调用InputDispatcher::handleReceiveCallback:
class LooperEventCallback : public LooperCallback {
public:
LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); }
private:
std::function<int(int events)> mCallback;
};
接着调用Looper的addFd函数去监听serverChannel对应的fd。
查阅资料:Looper提供了addFd函数用于添加需要监控的文件描述符,这个文件描述符由调用者指定,调用者还须指定对何种I/O(可读还是可写)事件进行监控。另外,也可指定用于处理可I/O事件时的回调处理函数(及其需用到的私有数据)。可在LooperCallback的子类中重载handleEvent来实现对可I/O事件的处理。因此,借助于Looper的pollOnce和addFd函数,可以实现对文件描述符的监控。无数据到来时pollOnce的调用者将睡眠等待,有数据到来时其被自动唤醒,并执行指定的回调处理者(若有的话)。
那么这里Looper会持续监听serverFd的接收缓冲区,如果有数据传输,就会触发LooperEventCallback的handleEvent回调,那么最终会调用InputDispatcher::handleReceiveCallback。
8 WindowState.openInputChannel
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputChannelToken = mInputChannel.getToken();
mInputWindowHandle.setToken(mInputChannelToken);
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) {
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);
}
}
Native层创建完服务端和客户端InputChannel后,最终会返回客户端InputChannel,接着JNI层的InputManagerService基于该C++层InputChannel对象生成了一个Java层的InputChannel对象,最终返回到了WindowState#openInputChannel处。
1)、将WindowState的成员变量mInputChannel赋值为返回的客户端InputChannel,这样WindowState就持有了客户端InputChannel。
2)、将WindowState的成员变量mInputChannelToken,该WindowState对应的InputWindowHandle的成员变量token,赋值为InputChannel的token,这样上层也持有了这个token。
3)、将键值对<IBinder mInputChannelToken, WindowState this>添加到WindowManagerService的mInputToWindowMap中,这样WindowManagerService便可以根据从Native层传来的token对象获取到对应的WIndowState对象。
4)、将mInputChannel拷贝给outInputChannel,最终返回给ViewRootImpl。
9 小结
1)、对于每一个新添加的窗口,InputDispatcher为其创建了一对socket,通过一对InputChannel封装,另外创建了一个IBinder类型的token,由这两个InputChannel共同持有。
2)、对于服务端InputChannel,InputDispatcher创建了一个Connection对象持有这个InputChannel,然后把键值对<IBinder token, Connection connection>加入到InputDispatcher的mConnectionsByToken中,这样后续可以通过token获取到Connection,进而拿到InputChannel。
3)、对于客户端InputChannel,除了将该InputChannel返回给ViewRootImpl之外,WMS也保存了相应的InputChannel和token。
4)、通过2和3的工作,该token将上层和Native层的窗口信息串联起来,上层可以根据从Native层传来的token,获取到相应的WindowState和客户端InputChannel。Native层可以根据从上层传来的token得到Connection,进而得到服务端InputChannel。
二、客户端InputEventReceiver的创建
上一步中,ViewRootImpl在调用Session#addToDisplayAsUser之后,就完成了InputChannel的初始化,此时的inputChannel代表的是客户端的InputChannel。
接下来分析ViewRootImpl里的InputEventReceiver是如何创建的。
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
然后基于这个客户端InputChannel生成一个WindowInputEventReceiver对象。
1 ViewRootImp.WindowInputEventReceiver.init
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
WindowInputEventReceiver是ViewRootImpl 的内部类,继承InputEventReceiver。
2 InputEventReceiver.init
/**
* Creates an input event receiver bound to the specified input channel.
*
* @param inputChannel The input channel.
* @param looper The looper to use when invoking callbacks.
*/
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();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
InputEventReceiver构造方法中又调用了nativeInit。
3 android_view_InputEventReceiver.nativeInit
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
std::shared_ptr<InputChannel> inputChannel =
android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
std::string message = android::base::
StringPrintf("Failed to initialize input event receiver. status=%s(%d)",
statusToString(status).c_str(), status);
jniThrowRuntimeException(env, message.c_str());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
基于传入的Java层的InputChannel对象找到Native层的InputChannel对象,然后以此构建出一个NativeInputEventReceiver对象:
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());
}
}
需要关注的有:
1)、这里mReceiverWeakGlobal保存的是Java层InputEventReceiver的引用。
2)、将InputChannel存储到mInputConsumer成员变量中,后续可以通过mInputConsumer取出这个客户端InputChannel。
最终调用initialize函数进行初始化。
4 NativeInputEventReceiver.initialize
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
ALOOPER_EVENT_INPUT定义在Looper.h中,表示当前文件描述符可读。
/**
* The file descriptor is available for read operations.
*/
ALOOPER_EVENT_INPUT = 1 << 0,
5 NativeInputEventReceiver.setFdEvents
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);
}
}
}
调用当前NativeInputEventReceiver的Looper的addFd函数监听客户端InputChannel对应的socket 的文件描述符,并且在有数据到来时执行相应回调,这里看到回调参数传入的是this。因为NativeInputEventReceiver是继承LooperCallback的:
class NativeInputEventReceiver : public LooperCallback {
那么一旦向服务端InputChannel保存的socket写入数据,则客户端这边的注册的NativeInputEventReceiver则触发handleEvent回调,这一点在分析InputDispatcher分发事件的时候再详细介绍。
三、总结
1)、在Native层创建了一个IBinder类型的token,一个用于服务端和客户端通信的socket对,每一个socket都由一个InputChannel进行封装,即Server端InputChannel和Client端InputChannel,这两个InputChannel持有对应socket 的文件描述符,在需要通信的时候先获取到相关InputChannel对象,然后取出其中保存的socket文件描述符,通过socket相关函数向其中写入数据来进行通信。
Server端InputChannel保存在InputDispatcher为其创建的一个Connection对象中,InputChannel的token和该Connection对象一一对应。
Client端InputChannel保存在窗口对应的WindowState中,InputChannel的token和该WindowState一一对应。
token很重要,它是在上层和Native层能够找到正确InputChannel的关键。
2)、Native层在创建完InputChannel对后,最终给Java层返回了一个客户端InputChannel,该InputChannel先是返回到WM,最终跨进程返回给ViewRootImpl。
3)、ViewRootimpl根据上一步得到的InputChannel创建了一个WindowInputEventReceiver对象,该对象在初始化的时候在JNI层构建了一个NativeInputEventReceiver对象并将客户端InputChannel传给了NativeInputEventReceiver,NativeInputEventReceiver内部有一个InputConsumer成员变量保存这个客户端InputChannel,NativeInputEventReceiver的Looper通过addFd函数对客户端socket进行监听,如果服务端socket有写入操作那么就回调NativeInputEventReceiver.handleEvent函数。