本文旨在介绍如何在Android设备上运行Xserver,使得Linux程序可以通过X11协议连接到Xserver,并实现X程序的窗口和Android应用窗口的融合,从而实现这些Linux应用程序在Android设备上的无缝运行。
原理介绍
Xserver是X Window系统的核心组件,负责管理显示设备和处理来自X客户端的请求。X11协议是X Window的通信协议,定义了客户端和服务器之间如何传输窗口操作和图形绘制指令。
Android的窗口系统由SurfaceFlinger和WindowManagerService管理,负责窗口的创建、绘制和交互。
将Xserver移植到Android上可以使用户在FDE设备上运行桌面级的Linux应用程序。这对于FDE上正在进行开发、测试或使用特定Linux应用的用户非常有用。例如,可以运行GIMP进行图像处理,或者运行WPS进行文档编辑。
为了在Android上运行Xserver,需要在Android系统中引入一个Xserver实例,并使其能够与Android的窗口系统交互。还需要编写一个桥接层,使Xserver能够与Android的SurfaceFlinger和WindowManagerService进行通信。
X11协议与Android窗口系统的交互
X11协议定义了客户端与服务器之间的通信方式,而Android的窗口系统则有自己的一套接口。需要编写一个适配层,将X11协议的指令转换为Android窗口系统的操作。例如,当X客户端请求创建一个窗口时,适配层需要在Android系统中创建一个对应的窗口,并将窗口的ID和属性反馈给X客户端。
窗口的坐标和生命周期管理
为了使X程序的窗口与Android窗口系统保持一致,需要管理窗口的坐标和生命周期。具体来说,当X客户端请求移动或调整窗口大小时,适配层需要将这些请求转换为Android窗口系统的操作。此外,还需要监控Android窗口系统的事件,及时更新X客户端的窗口状态。
相对于VNC的优势
使用高效的图形绘制库,如OpenGL ES,提高图形渲染性能。Xserver的渲染相关扩展实现了高效的图像通信,减少延迟和带宽占用。
应用融合方案实现
本方案是运行在OpenFDE的一个APP,”FDE:X11”,主要的结构如下图所示,分为三个主要部分,Xserver,WindowManager,Activity + SurfaceManager ,下面分别说明各个模块实现的功能。
Xserver
OpenFDE平台的Xserver运行在一个独立进程的Android service(XWindowService)里,在这个service中通过native函数启动一个TCP服务端或者unix socket服务端,支持X11协议的Linux程序可以与这个服务端连接,向其发送绘图指令和接收输入事件。Xserver上的许多扩展和补充协议使得它目前还是Linux上兼容性和效率最好的显示方案,支持本地和远程连接,很小的内存占用。在Android NDK中的编译调试也比较方便,没有多余的依赖,还有不少成熟的方案可以借鉴。
WindowManager
大部分的Linux桌面环境都需要窗口管理器,用于管理和控制 X Window System (X11) 窗口的程序。它处理窗口的打开、关闭、移动、调整大小等操作,并决定窗口的外观和行为。OpenFDE上Xserver连接的是自定义开发的一个基础版窗口管理器,运行在FDE-X11中,没有窗口修饰功能,额外实现合成器、窗口属性同步、剪贴板同步等功能。
Compositor图像重定向
在窗口管理器中启用了Compositor扩展,Composite扩展允许窗口管理器在所有窗口创建和显示的过程中重定向窗口输出内容。FDE-X11使用了这三个API RedirectSubwindows,NameWindowPixmap和GetWindowPixmap,通过RedirectSubwindows调用将一个窗口树中的所有窗口渲染重定向到内部存储off-screen storage 。重定向的时候可以指定让X 自动更新窗口的内容到屏幕上或者由混成器手动更新,通过NameWindowPixmap取得某个窗口的内部存储,再通过GetWindowPixmap最终拿到这个窗口的图像buffer。合成器实现窗口特效的原理也基于此。
这样就可以将每个窗口的图像buffer分离出来,转到Android处理绘制,而不再是只绘制整个Xserver的合成后的图像buffer。
Activity或Dialog
在目前的FDE-X11方案中,主要还是使用Activity做为与Linux一一对应的窗口。在Android的窗口系统中,APP窗口所处理的层次被限定在一个层级范围,只有在这个层级范围的窗口才会与其他APP的窗口交互行为相同,目前可选的Android组件就只有Activity和Dialog了。作用是创建SurfaceView显示输出图像,接收Android输入事件发送给Xserver。在FDE使用的Android freeform模式下,表现大致等同于Linux桌面,在理论上甚至可以做到完全一致。
在Linux窗口创建的时,在Android中创建一个Activity,将Linux窗口的图像显示在这个Activity的SurfaceView上,如果Activity有按键或者鼠标输入就发送到Xserver对应的坐标。这个过程需要处理的问题非常多,比如生命周期同步,窗口类型,事件重定向,窗口图标和标题栏操作等等。
SurfaceManager
SurfaceManager是FDE-X11抽象出来的一个模块,用来管理绘制buffer的所有Surface。将不同的Surface传到Xserver的EGL环境中,使不同的Surface正确绘制不同的buffer。对于一些OverrideRedirect的窗口,将不会新建Surface,而绘制在它所依赖的窗口上。
Android和Linux窗口属性同步
X窗口除了图像buffer,本身的元属性和扩展属性也非常多,最基本的比如尺寸、坐标、层级、支持的操作等等。就是要Android的窗口特性要匹配X窗口的特性,在Linux上可以怎么操作窗口,在FDE上就可以怎么操作窗口。并且两端窗口的属性状态都是实时匹配的,比如在Android中手动调整了窗口大小,窗口管理器就执行ConfigureWindow同步更新X窗口大小,如果Linux程序调整了窗口大小,Android中就通过ActivitiyTaskManagerService来resizeTask。
Android和Linux剪贴板同步
在上一个使用VNC原理的融合方案中,需要将Android输入法产生的字符传到VNCServer再发送到Linux输入法,而这个方案中Xservr本身就有这样的接口,可以直接发送字符到Xserver。剪贴板的支持则是将Android的ClipBoardManager和Xserver的Selection相关协议对接起来就可以实现。在FDE-X11中,采用的方法是新建一个Window做为剪贴板管理者,实时接收和发送剪贴板内容,并且将这个内容与Android端同步,即实现了两端使用的是同一个剪贴板的效果。
总结
与现在的各个平台的Xserver实现不同,FDE-X11因为本身是一个桌面环境,借助AOSP的强大,也借鉴了一些开源方案(比如Termux:X11),实现了这个特殊的方案,简单对比效果如下图:
FDE-X11
Termux:X11
本方案研究开发历时不短,但是还有不少不成熟之处,比如X窗口的复杂特性只实现了其中一部分,还有Android的窗口受限于framework的原始设计,改动起来也工作量巨大,尤其生命周期耗时与X窗口相比高出一两个数量级,还有应用效率更高wayland协议,这些都给优化用户体验留下很大空间。