当前位置:   article > 正文

Android 11 输入系统之InputDispatcher和应用窗口建立联系

Android 11 输入系统之InputDispatcher和应用窗口建立联系

InputDispatcher把输入事件传给应用之前,需要和应用窗口建立联系,了解了这个过程,就清楚了APP进程和InputDispatcher线程也就是SystemServer进程之间是如何传输数据了
我们向窗口addView的时候,都会调用到ViewRootImpl的setView方法,从这个方法开始分析(只关注和input有关的流程)

//frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
	synchronized (this) {
		if (mView == null) {
			//省略
			InputChannel inputChannel = null;
            if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
            	inputChannel = new InputChannel();//1
            }
            try {
  				mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                adjustLayoutParamsForCompatibility(mWindowAttributes);
                res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);//2
                setFrame(mTmpFrame);
			//省略
			if (inputChannel != null) {
            	if (mInputQueueCallback != null) {
           			mInputQueue = new InputQueue();
          			mInputQueueCallback.onInputQueueCreated(mInputQueue);
                 }
            	mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());//3
           }
           //省略
		}	
	}
}
  • 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

注释1处新建一个InputChannel 对象。注释2处是一个远程调用,也就是服务端的addToDisplayAsUser方法,注意inputChannel参数在aidl文件中标记的是out,说明inputChannel是根据远端返回的数据初始化的。注释3处创建WindowInputEventReceiver对象。
先来看看addToDisplayAsUser方法

//frameworks\base\services\core\java\com\android\server\wm\Session.java
	@Override
    public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
                outInsetsState, outActiveControls, userId);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

直接调用WMS的addWindow方法

//frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
	//省略
	final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, 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);
	}
	//省略
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

调用WindowState的openInputChannel方法

//frameworks\base\services\core\java\com\android\server\wm\WindowState.java
void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//1
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mWmService.mInputManager.registerInputChannel(mInputChannel);//2
        mInputWindowHandle.token = mInputChannel.getToken();
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);//3
            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);
        }
        mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

该方法主要完成以下工作:

  1. 创建socketpair,得到两个文件句柄,分别封装在InputChannel对象中
  2. 因为wms和inputDispatcher都是在SystemServer进程中,所以其中一个InputChannel即mInputChannel 只要直接注册就行了,不需要跨进程通信
  3. 将mClientChannel复制给outInputChannel,用于回传给APP应用进程

socketpair的创建过程

//frameworks\base\core\java\android\view\InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {
	//省略
	return nativeOpenInputChannelPair(name);
}
  • 1
  • 2
  • 3
  • 4
  • 5

java层的InputChannel只是一个壳,直接发起JNI调用

//frameworks\base\core\jni\android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    ScopedUtfChars nameChars(env, nameObj);
    std::string name = nameChars.c_str();

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);//1

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
   
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
    
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
    
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);//放入元素
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);//放入元素
    return channelPair;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

注释1处创建socketpair,并创建两个C++层的InputChannel对象

//frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {//创建socketpair
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.c_str(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }
	
	/*设置buffer的大小为32k*/
    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();//创建token

    std::string serverChannelName = name + " (server)";
    android::base::unique_fd serverFd(sockets[0]);
    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);//创建服务端InputChannel对象

    std::string clientChannelName = name + " (client)";
    android::base::unique_fd clientFd(sockets[1]);
    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);//创建客户端InputChannel对象
    return OK;
}
  • 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

可以看出,通过socketpair的两个fd,分别构造了Native层的InputChannel对象,同时,这个token也很重要,用户窗口中InputWindowInfo的token和这个是一致的
两个InputChannel构造完成后,其中一个需要通过binder回传给APP客户端(实际上就是写fd,然后客户端根据fd重新创建InputChannel),接下来分析服务端以及客户端的处理

注册InputChannel到InputDispatcher中

回到openInputChannel方法,InputChannel构造完成后,调用registerInputChannel,将服务端的InputChannel注册到InputDispatcher中

public void registerInputChannel(InputChannel inputChannel) {
	//省略
 	nativeRegisterInputChannel(mPtr, inputChannel);
}
  • 1
  • 2
  • 3
  • 4

也是直接调用JNI方法

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);//取出InputChannel
    
    status_t status = im->registerInputChannel(env, inputChannel);//1

    //省略
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

注释1处,调用NativeInputManager的registerInputChannel方法

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

继续调用InputDispatcher的registerInputChannel方法

//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
    { // acquire lock
       //省略
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);

        int fd = inputChannel->getFd();
        mConnectionsByFd[fd] = connection;
        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

首先根据inputChannel创建了一个Connection对象,然后取出inputChannel的fd,将该connection放入mConnectionsByFd数组,注意,数组的下标是fd,可以根据fd找到这个connection。同时把inputChannel放入mInputChannelsByToken数组
最后将该fd加到Looper中,Looper也是使用的epoll机制,当客户端写入事件时(主要是告知输入事件处理完毕),这个fd就表明有事件读入,就会调用handleReceiveCallback函数

客户端处理InputChannel

回到setView方法,客户端接收到服务端返回的InputChannel后,创建WindowInputEventReceiver对象,WindowInputEventReceiver继承自InputEventReceiver

//frameworks\base\core\java\android\view\InputEventReceiver.java
  public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        //省略
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);//1

        mCloseGuard.open("dispose");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注释1处调用nativeInit方法

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);//取出
   //省略

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);//创建NativeInputEventReceiver对象
    status_t status = receiver->initialize();//1
   //省略
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注释1处调用NativeInputEventReceiver的initialize方法,在initialize方法中直接调用setFdEvents,将客户端inputChannel 中的fd加到Looper中

//frameworks\base\core\jni\android_view_InputEventReceiver.cpp
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);//注意第4个参数为当前的NativeInputEventReceiver对象
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

添加到Looper后,如果后续有事件到来,即InputDispatcher发送过来了输入事件,则会调用NativeInputEventReceiver自己的handleEvent方法

总结

可以看出InputDispatcher和客户端进程之间通讯是采用socket的方式,而因为这里已经明确是是它们两个之间通讯,所以这里使用了更加方便的socketpair,socketpair得到两个fd分别给InputDispatcher和客户端进程,其中使用binder将其中的一个fd回传给客户端。

InputDispatcher由于和wms是在同一个进程,所以可以直接使用这个fd。InputDispatcher会创建Connection对象,并维护两个数组。并将fd添加到Looper中。客户端拿到这个fd也同样是加到Looper中。

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/574589
推荐阅读
相关标签
  

闽ICP备14008679号