Android7 Input(七)App与input系统服务建立连接

概述

        本文主要讲述Android 系统创建窗口时与输入管理系统服务通过InputChannel通道建立通信桥梁的过程。

本文涉及的源码路径

frameworks/native/libs/input/InputTransport.cpp

frameworks/base/core/java/android/view/InputChannel.java

frameworks/base/core/java/android/view/ViewRootImpl.java

frameworks/base/core/jni/android_view_InputChannel.cpp

frameworks/base/core/jni/com_android_server_input_InputManagerService.cpp

frameworks/base/services/core/java/com/android/server/wm/Session.java

frameworks/base/core/java/android/view/WindowState.java

App创建InputChannel

        关于Android系统中的窗口与窗口视图相关的内容,我们有机会再进行讲述,我这里只描述创建视图时与输入相关的部分。方法是setView, 如下所示:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
              ......
              // 如果窗口可接受输入事件,创建InputChannel输入通道
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                   ......
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    ......
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

               ......
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
                ......
            }
        }
    }

setView方法关于属于的主要逻辑如下:

1、创建一个空的InputChannel通道;

2、mWindowSession.addToDisplay中对InputChannel进行初始化;

3、注册App窗口收到InputChannel通道数据的事件回调;

InputChannel通道的真正初始化由mWindowSession.addToDisplay完成,mWindowSession是IWindowSession,属于AIDL的服务代理端,它最终调用的是IWindowSession中的服务实现端,IWinodwSession由Session实现如下所示:

final class Session extends IWindowSession.Stub {
    @Override
    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);
    }
}

然后进一步调用了mService.addWindow, 而mService是WindowManagerService系统服务。因此mWindowSession.addToDisplay通过层层调用,最终调用到了WindowManagerService中的addWindow完成App和Input系统服务建立连接,如下所示:

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {

           ......
            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
        return res;
    }

这部分的代码涉及的内容比较复杂,我们只保留与窗口输入相关的部分,从代码中可以看到,由WindowState.openInputChannel方法进一步完成InputChannel的初始化,代码如下所示:

WindowState.java

 void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = makeInputChannelName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            // 将mClientChannel的native对象绑定到outInputChannel的native对象
            mClientChannel.transferTo(outInputChannel);
            // 删除mClientChannel自己的native对象
            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);
    }

该方法的主要逻辑如下:

1、App 创建Inputchannel通道, 用自己Client的InutChannel[0],初始化outputChannel通道;

2、将InutChannel[1] 注册到input系统服务中的InputDispatcher中;

注册服务端InputChannel到InputDispatcher

        上一章节,我们讲述了App创建输入通道的过程,本章节我们讲述App如何与input系统服务建立通信的桥梁的过程。建立的过程,我们可以把它抽象成linux中pipe管道,一端为App所持有outChannel,另外一端inputChannels[1]通过,mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle)的注册,让input系统服务持有。主要实现是通过调用InputManagerService中的registerInputChannel,如下所示:

 public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null.");
        }

        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }

该方法进一步直接调用nativeRegisterInputChannel JNI方法如下所示:

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    ......
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        String8 message;
        message.appendFormat("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return;
    }
    ......
}

然后继续追踪,最终调用到了我们熟悉的InputDispatcher中的registerInputChannel,如下所示:

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
     ......    
    { // acquire lock
        AutoMutex _l(mLock);

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

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

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

该方法的实现逻辑如下:

1、为每一个请求注册Inputchannel创建一个Connection进行管理,并将待注册的InputChannel的通道描述符和Connection建立关联容器映射;

2、将注册的InputChannel通道加入到InputDispatcher中Looper事件监控,当App向客户端的InputChannel写入信息时,则触发InputDispatcher中的handleReceiveCallback回调方法的调用,关于App和Input系统服务的通信过程,我们后续的章节进行讲述。

App注册事件回调

上一个章节,我们讲述了App和input系统服务建立通信连接的过程,那InputDispatcher发送msg时,App如何接收呢,核心实现如下所示:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    
    .......
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
}

当InputDispacther向InputChannel写入事件信息时,App最终通过WindowInputEventReceiver进行事件的处理,App接受input事件和处理流程我们在下一篇进行讲述。

总结

        本文主要讲述了App初始化创建窗口时的与输入管理相关的处理流程,App为了能接收到系统上报的输入事件,通过InputChannel作为桥梁,将App和Input系统服务建立了通信桥梁,实现的核心原理就是创建socket对,并通过事件监听的方式,从socket中获取监听的信息,并进行处理,当App处理完信息也则通过socket对,告诉input系统服务的处理结果。后续章节我们会讲述这个处理流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值