2【Android 12】客户端和服务端InputChannel创建

在这里插入图片描述

输入事件最终还是要发送给窗口去处理的,那么我们就看看在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函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值