zoukankan      html  css  js  c++  java
  • InputChannel 和 InputWindowHandle

    InputChannel

    应用程序窗口需要获取输入事件,就需要和IMS建立一个传输通道(WMS替我们做了),这个通道就是InputChannel

    InputChannel本质就是local unix domain socket,创建时,用到了socketpair函数,其作用见man手册。

    man socketpair

    应用程序调用和InputChannel产生联系的函数调用流程如下:

    //ViewRootImpl.java
    void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    	...
        if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
        	mInputChannel = new InputChannel();
    	}
        ...
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
        ...
    }
    
    //Session.java 前者通过Binder 最终到这里
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
    	return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
    		outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
    
    // WindowManagerService.java
    public int addWindow(Session session, IWindow client, int seq,
    	WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
    	Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
    	InputChannel outInputChannel) {
    	...
    	final boolean openInputChannels = (outInputChannel != null
    			&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    	if  (openInputChannels) {
    		win.openInputChannel(outInputChannel);
    	}
        ....
    }
    

    win的实际类型是WindowState,通过其openInputChannel方法,我们会注册一个InputChannelIMS,同时返回一个到outInputChannel变量。通过outInputChannel,我们就能从IMS获取到输入事件,同时也能发送数据到IMS

    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;
    	} else {
    		// If the window died visible, we setup a dummy input channel, so that taps
    		// can still detected by input monitor channel, and we can relaunch the app.
    		// Create dummy event receiver that simply reports all events as handled.
    		mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
    	}
    	mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }
    

    openInputChannelPair返回两个InputChannel,可以将其看做是全双工的匿名管道(底层实现是socketpair)。将其中一个返回给ViewRootImpl::mInputChannel,将另一个注册到IMS,这样,ViewRootImpl就能够处理输入事件了。

    pipe是半双工的,返回的两个fd,一个用于读,一个用于写。

    mkfifo,命名管道,创建一个文件,使用者打开文件,然后对其读写。(全双工?)

    InputChannel创建

    通过静态成员函数openInputChannelPair创建。

    // InputChannel.java
    public static InputChannel[] openInputChannelPair(String name) {
    	...
    	return nativeOpenInputChannelPair(name);
    }
    
    //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);
    
        jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
        //注意这里,用 InputChannel 来构建了一个NativeInputChannel
        jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
                std::make_unique<NativeInputChannel>(serverChannel));
    
        jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
                std::make_unique<NativeInputChannel>(clientChannel));
        env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
        env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
        return channelPair;
    }
    

    创建两个NativeInputChannel对象,然后初始化两个Java InputChannel对象。

    Java InputChannel有一个long mPtr成员变量,其保存着NativeInputChannel(内部持有InputChannel的引用)的内存地址,通过这个地址,后续通过JNI进入Naitve时,就能找到这里创建的InputChannel了。

    Native InputChannel的创建

    也是通过静态成员函数(openInputChannelPair)创建。

    status_t InputChannel::openInputChannelPair(const String8& name,
            sp<InputChannel>& outServerChannel, sp<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=%d",
                    name.string(), errno);
            outServerChannel.clear();
            outClientChannel.clear();
            return result;
        }
    // Socket buffer size.  The default is typically about 128KB, which is much larger than
    // we really need.  So we make it smaller.  It just needs to be big enough to hold
    // a few dozen large multi-finger motion events in the case where an application gets
    // behind processing touches.
        //static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
        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));
    
        String8 serverChannelName = name;
        serverChannelName.append(" (server)");
        outServerChannel = new InputChannel(serverChannelName, sockets[0]);
    
        String8 clientChannelName = name;
        clientChannelName.append(" (client)");
        outClientChannel = new InputChannel(clientChannelName, sockets[1]);
        return OK;
    }
    

    socketpair返回的两个文件描述符,对我们来说是无法区分的,所以,这里 分别为其指定了一个名称,用来区分其作用。创建完了,接下来就是注册到IMS了。

    注册 InputChannelIMS

    前面的WindowState中,在创建了InputChannel后,会调用registerInputChannel将其注册到IMS中。

    WindowStateWMS中创建,IMSWMS都位于SystemServer进程,所以,这里可以直接调用IMS::registerInputChannel进行注册。

    public void registerInputChannel(InputChannel inputChannel,
    	InputWindowHandle inputWindowHandle) {
        // mPtr对应的是 NativeInputManager
    	nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }
    

    既然是通过JNI进入Native,那么直接看一下NativeInputManager::registerInputChannel的实现:

    
    status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
            const sp<InputChannel>& inputChannel,
            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
        return mInputManager->getDispatcher()->registerInputChannel(
                inputChannel, inputWindowHandle, monitor);
    }
    

    Oh!!! 去到了InputDispatcher,也是能够理解的,毕竟事件的分发就是由InputDispatcher完成的。。。

    一开始看到InputDispatcher的构造函数中出现了Looper还有点懵,现在知道了。。

    status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
        { // acquire lock
            AutoMutex _l(mLock);
    
            if (getConnectionIndexLocked(inputChannel) >= 0) {
                ALOGW("Attempted to register already registered input channel '%s'",
                        inputChannel->getName().string());
                return BAD_VALUE;
            }
    
            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);
        } // release lock
    
        // Wake the looper because some connections have changed.
        mLooper->wake();
        return OK;
    }
    

    最后就是将InputChannel保存到了Connection中,然后InputDispatcher中持有一个:

    //InputDispatcher.cpp
    // All registered connections mapped by channel file descriptor.
    KeyedVector<int, sp<Connection> > mConnectionsByFd;
    

    数据发送和接收

    InputChannel的数据发送和接收对应两个函数:

    tatus_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 OK;
    }
    
    

    为什么贴这啷个函数呢?

    我是觉得对于数据send/recv这两个函数可以直接copy过来用,对于错误信息的处理,比较完善了,

    我平时也只会处理一个EAGAIN

    InputWindowHandle

    前面WindowState::openInputChannel中注册InputChannelIMS时,还有一个InputWindowHandle,后面到InputDispatcher中,也用到了这个。他的作用是什么??

    InputWindowHandle创建

    // WindowState的构造函数中创建 
    mInputWindowHandle = new InputWindowHandle(
                    mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
                        getDisplayId());
    
    // 构造函数
    public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
    		Object windowState, IWindow clientWindow, int displayId) {
    	this.inputApplicationHandle = inputApplicationHandle;
    	this.windowState = windowState;
    	this.clientWindow = clientWindow;
    	this.displayId = displayId;
    }
    

    这里主要介绍一下IWindow,由AIDL生成。对WMS而言,其就是Window的客户端,WMS通过IWindow就通知Window有事件发生。

    oneway interface IWindow {
        /**
         * Drag/drop events
         */
        void dispatchDragEvent(in DragEvent event);
        
        /**
         * Tell the window that it is either gaining or losing focus.  Keep it up
         * to date on the current state showing navigational focus (touch mode) too.
         */
        void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
        ...
    }
    

    NativeInputWindowHandle

    继承至InputWindowHandle

    主要有两个成员变量。

    InputWindowInfo* mInfo; //InputWindowHandle
    jweak mObjWeak; //NativeInputWindowHandle
    
    • InputWindowInfo中的很多成员变量和Java端的InputWindowHandle是对应的。
    • mObjWeak就是对Java端的InputWindowHandle的一个弱引用。

    通过InputWindowHandle::updateInfo就能够更新mInfo中的成员值(通过jni获取java端对象的成员值)。

    废话这么多,还是不晓得这个东东是干嘛用的,大胆猜测一下,通过这个,我们能判断事件需要转发给那些窗口处理!!待补充。。。。

    总结

    InputChannelInputWindowHandle从Java层到JNI再到Native层,大概的关系如下:

    image-20201216000957215

    WMS中创建两个InputChannel(Java层定义)对象后,一个返回给调用端,一个注册到IMS(终点就是InputDispatcher),同时注册下来的还有InputWindowHandle

    • 返回给WindowInputChannel对应的Native类型是NativeInputChannel(JNI中定义)。
    • 注册到IMS中的实际类型就是InputChannel(libinput中定义)。

    InputDispatcher::registerInputChannel中,注册的InputChannelInputWindowHandle都保存到了Connection对象中。Connection对象最终又保存到了InputDispatcher::mConnectionsByFd中。

    原本在看InputDispatcher的时候好奇InputChannel是个什么玩意,结果,就入了这个坑,记录一下。。。

    疑问

    NativeInputChannel的作用是啥???直接使用InputChannel不行吗???

    包了那么多,最终也注册到IMS的还是InputChannel

    补充

    InputChannel在哪里接收数据

    注册到InputDispatcher时,有将InputChannel::mFd添加到Looper中。

    mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    

    当往ViewRootImpl::mInputChannel写入数据时,Looper就会执行InputDispatcher::handleReceiveCallback进行处理。

    InputChannel在哪里发送数据

    答案就是InputPublisher

    class InputPublisher {
    public:
        /* Creates a publisher associated with an input channel. */
        explicit InputPublisher(const sp<InputChannel>& channel);
         */
        status_t publishKeyEvent(
                uint32_t seq,
                int32_t deviceId,
                int32_t source,
                int32_t action,
                int32_t flags,
                int32_t keyCode,
                int32_t scanCode,
                int32_t metaState,
                int32_t repeatCount,
                nsecs_t downTime,
                nsecs_t eventTime);
    
    private:
        sp<InputChannel> mChannel;
    };
    

    看一下前面的Connection类的成员变量:

    sp<InputChannel> inputChannel; // never null
    sp<InputWindowHandle> inputWindowHandle; // may be null
    InputPublisher inputPublisher;
    

    Connection的构造函数中,会使用inputChannel来初始化InputPublisher::mChannel(InputDispatcher::registerInputChannel中完成)。

    然后,当InputDispatcher需要分发按键事件时,就会通过Connection::inputPublisher::publishKeyEvent来完成。

    status_t InputPublisher::publishKeyEvent(
            uint32_t seq,
            int32_t deviceId,
            int32_t source,
            int32_t action,
            int32_t flags,
            int32_t keyCode,
            int32_t scanCode,
            int32_t metaState,
            int32_t repeatCount,
            nsecs_t downTime,
            nsecs_t eventTime) {
        if (!seq) {
            ALOGE("Attempted to publish a key event with sequence number 0.");
            return BAD_VALUE;
        }
    
        InputMessage msg;
        msg.header.type = InputMessage::TYPE_KEY;
        msg.body.key.seq = seq;
        msg.body.key.deviceId = deviceId;
        msg.body.key.source = source;
        msg.body.key.action = action;
        msg.body.key.flags = flags;
        msg.body.key.keyCode = keyCode;
        msg.body.key.scanCode = scanCode;
        msg.body.key.metaState = metaState;
        msg.body.key.repeatCount = repeatCount;
        msg.body.key.downTime = downTime;
        msg.body.key.eventTime = eventTime;
        
        //完成数据发送工作
        return mChannel->sendMessage(&msg);
    }
    
  • 相关阅读:
    之前没弄明白的链表和return
    读Bsautiful Soup库有感
    Beautiful Soup库
    XML和JSON的系列操作
    urllib和requests的区别(持续更新)
    request请求把cookie当作参数传入请求
    Python统计文件夹下文件的个数
    基础算法之查找
    timeit用法(不完整)
    spring初识
  • 原文地址:https://www.cnblogs.com/liutimo/p/14141851.html
Copyright © 2011-2022 走看看