• Android系统--输入系统(十六)APP跟输入系统建立联系_InputChannel和Connection


    Android系统--输入系统(十六)APP跟输入系统建立联系_InputChannel和Connection

    0. 核心:socketpair机制

    1. 回顾Dispatch处理过程:

    1.1 放入队列前稍加处理

    • 分类:Global Key/System Key/User Key
    • 处理紧急事件(比如来电的时候按下音量键静音)

    1.2 InputReader线程将读到的输入事件稍加处理后放入mInboundQueue队列中,接着唤醒Dispatch线程

    1.3 Dispatch从mInboundQueue队列中将输入事件取出,并稍加处理。

    • 对于Global Key/System Key按键处理:放入mCommandQueue队列中,依次处理,处理之后就release
    • 对于User Key,放入队列:查找目标APP,得到Connection,放入其OutBoundQueue队列中,稍后取出处理

    2. 引入--如何找出目标应用程序,输入系统和应用程序如何建立联系?

    PC和安卓系统都是运行着多个应用程序,但是只有屏幕最前面的应用程序才可以接收到输入事件,谁来告诉输入事件哪个是运行在屏幕最前面的应用程序呢?

    2.1 引入WindowServiceManager窗口管理服务

    • 对于每一个应用程序,WindowServiceManager都有一个结构体WindowSate来表示该应用程序。假设新启动一个APP,通过binder通信,调用addToDisplay,会导致AddWindow被调用,AddWindow创建了WindowSate表示该应用程序,接着创建一个socketpair得到两个文件句柄fd0和fd1,fd1直接返回给应用程序,fd0将其封装为InputChannel类,一方面InputChannel会放进该APPWindowState中,另外一方面,他会将InputChannel注册给InputDispatch。

    2.2 Dispatch线程

    • InputDispatch线程中里面有KeyedVector包含含有多个connection,这些connection可以通过注册实现,创建一个connection(含有InputChannel,InputChannel含有文件句柄fd),将创建好的connection放入Vector当中。
    // All registered connections mapped by channel file descriptor.
    KeyedVector<int, sp<Connection> > mConnectionsByFd;
    

    2.3 引入connection

    • 对于每一个能够接受输入事件应用程序,在InputDispatch当中都有一个connection。假设还有一个应用程序APP,在WindowServiceManager中也有一个WindowState,在InputDispatch中也有一个对应的connection,放入Vector容器中,在connection中含有InputChannel和fd,其中fd来自Socketpair的fd0,另外一个fd1通过Binder通信返回给APP4。这样子InputDispatch线程想把输入事件发送给APP时候,先要找出当前在屏幕最前面的应用程序,然后从vector容器中找到他的connection,然后将数据写入fd当中既可。

    2.4 总结

    • Dispatch线程从输入事件中读到数据后,可以通过Vector当中找出某个connection,把输入事件放入fd当中,另外一个应用程序可以从另外一个文件句柄中得到输入事件,将得到的文件句柄封装为InputChannel,再封装为WindowInputEventReceiver,最后把fd放入Looper中,使用Epoll机制查询等待数据,具体可以见下面关系图。

    2.5 补充

    • InputReader线程、InputDispatch线程和WindowServiceManager都处于System进程当中,故这三个线程可以直接通信不需要Binder介入,而APP如果想跟这三个线程通信则需要通过Binder机制来实现或者通过事先建立好的Socketpair再通过Binder将文件句柄返回给APP,故其文件句柄可能发生变化。

    3. APP获得SocketPair的fd过程分析

    • 发起addToDisplay的操作
    ViewRootImpl.java
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
    getHostVisibility(), mDisplay.getDisplayId(),
    mAttachInfo.mContentInsets, mInputChannel);
    
    • 导致同文件下的onTransact被调用
    IWindowSession.java
    mRemote.transact(Stub.TRANSACTION_addToDisplay, _data, _reply, 0);
    
    • 根据code值调用本地的addToDisplay
    IWindowSession.java
    int _result = this.addToDisplay(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6);
    
    - 得到两个文件句柄
    
    Session.java
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
    int viewVisibility, int displayId, Rect outContentInsets,
    InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
        outContentInsets, outInputChannel);
    }
    
    WindowManagerService.java
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    
    - 把fd1写给InputChanne
    
    WindowManagerService.java
    inputChannels[1].transferTo(outInputChannel); //outInputChannel含有fd1,实际上就是arg6
    
    • 根据_arg6的返回结果,把fd写给Binder驱动程序
    IWindowSession.java
    _arg6.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    
    - 最终调用该函数把fd写入bind驱动中
    
    android_view_InputChannel.cpp
    parcel->writeDupFileDescriptor(inputChannel->getFd());
    
    • 远程操作之后,从驱动程序读出fd
    outContentInsets.readFromParcel(_reply);
    
    - 从Binder驱动中读出fd
    
    android_view_InputChannel.cpp
    int rawFd = parcel->readFileDescriptor();
    int dupFd = dup(rawFd);
    

    4. 具体调用过程时序图

    具体源码可以根据下面的时序图进行具体分析。注:引用韦东山老师

  • 相关阅读:
    datetime和time的时间戳用法
    ER图
    python update()
    理解JWT(JSON Web Token)认证及python实践
    python lambda匿名函数 用法
    flask_restful(转载)
    Flask-SQLALchemy
    创建只有一个元素的元组
    MySQL安装过程中显示无法启动
    聚类
  • 原文地址:https://www.cnblogs.com/lkq1220/p/7211235.html
Copyright © 2020-2023  润新知