ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

统信UOS上运行安卓应用:Anbox容器方案与内核编译实战

统信UOS上运行安卓应用:Anbox容器方案与内核编译实战 前阵子一个客户给我提了个需求说在统信UOS桌面系统上要跑一个安卓端业务软件问能不能搞定。我第一个想到的就是Anbox——毕竟在Linux上跑Android应用绕来绕去最终还是回到容器方案。UOS系统本身是基于Debian的技术路线底层是Linux内核Anbox需要binder和ashmem这类内核特性而标准UOS内核默认没有把这些模块完整打开所以从内核编译到应用安装这条路几乎是绕不开的。这篇攻略我尽量写成可以直接照做的操作手册把我踩过的坑、试过的路径、最后能跑通的方案都放进来。先说清楚这篇内容适合谁在国产化替代项目里被要求“在UOS上跑安卓App”的工程师或者纯粹想在Linux桌面上无缝用几个安卓工具软件的技术爱好者。看完你不仅能装上一个能跑安卓App的Anbox还能自己动手编译内核模块、解决容器启动和APK安装过程中的各种报错。1. 为什么我要在UOS上折腾Anbox项目背景与核心思路1.1 在Linux桌面跑Android应用的几种主流思路Android是一个独立的移动操作系统它的用户态、HAL层、内核驱动都跟PC的Linux桌面有很大差异。想在UOS上运行安卓APP市面上主流思路大概是几类。第一类是用模拟器比如Android Studio的AVD、雷电模拟器的Linux版、Genymotion。这类方案本质上是虚拟机外加硬件加速设计上偏开发调试对图形渲染和内存占用都不友好要在UOS这种国产系统上跑通显卡驱动就是一道坎。第二类是应用兼容层方案类似Wine翻译Windows那样去翻译Android应用典型代表是“安集”这类商业产品效果不稳定视频和图形密集的应用基本没戏。第三类就是我最后选用的容器方案Anbox和Waydroid都属于这一类它们共享宿主机内核把Android当成一个用户空间容器来跑性能和资源占用都有天然优势。选择Anbox而不是Waydroid原因很简单Anbox的项目成熟度更早官方文档和社区资料更全而且它在x86_64架构下的apt和snap安装方式相对顺手。Waydroid确实更现代化、性能更好但Waydroid需要内核支持BinderFS、Wayland等新特性在UOS官方内核上折腾起来比Anbox更麻烦。1.2 Anbox的技术原理与适用边界Anbox全称是Anbox in a Box它把安卓系统里面一大堆底层组件做了替换让Android App运行在跟Linux宿主共享一个内核的容器里。Android系统正常的运行方式是在自己的内核上驱动硬件Anbox的做法则是把Android中的binder IPC通信、ashmem内存管理这些跟内核耦合比较重的部分直接映射到宿主机内核容器里跑的其实是完整的Android用户空间。binder本身就是Android整体架构的设计精髓几乎所有的跨进程通信都由它承担。如果你用过Intel Houdini运行ARM应用或者刷过Magisk模块你应该对binder不陌生。ashmem则是Android早期用于共享内存腾挪的机制Anbox运行过程中大量涉及匿名共享内存。这两个内核特性在标准Linux内核里虽然代码都有但很多发行版默认不会编译进内核我们需要自己编译。Anbox的定位不是游戏专用机。要说玩《原神》或者各种大型3D手游Anbox会非常吃力。它的优势场景是轻量应用比如办公软件、支付类客户端、IM工具还有政务行业那些只出了安卓版但必须在国产PC上使用的内部业务App。所以如果你接到的是“把手机端App跑到电脑上”这种需求Anbox是性价比很高的方案。1.3 为什么“内核编译”绕不过去安装Anbox的时候很多人第一步就被卡住装完Anbox发现/dev/binder、/dev/ashmem根本不存在容器起不来。原因很简单Anbox要求内核同时开启CONFIG_ANDROID_BINDER_IPC、CONFIG_ANDROID_ASHMEM并且在系统中暴露对应的设备节点。UOS官方内核为了兼容性默认是没有打开这两项配置的。用户要么用内核模块补充加载这两个特性要么直接重新编译一个带上述选项的新内核。在实际项目里完整的重编内核工作量很大而且UOS的更新策略会让你面临每次内核升级就“打回原形”的窘境。所以我选择的路线是保留官方内核针对当前内核版本编译对应的binder、ashmem内核模块用modprobe的方式加载。这在快速交付上很划算。当然你把整个内核源码拉下来重新编译也不是不行尤其是当你同时想砍掉一些不需要的内核特性来提升性能时。下面第三章我会把两种路线都写上你自己按项目周期选。2. 动手前的准备开启开发者模式与部署编译环境2.1 UOS开发者模式这一步直接决定后续操作能不能继续UOS跟其它Linux发行版最不一样的地方就在于它默认状态下不允许用户随意修改系统关键文件。如果你直接sudo mount、sudo insmod或者往/usr/lib里复制文件会有各种阻碍。这是因为UOS默认开启了系统安全管控。要解决这个问题必须先在“设置 → 关于本机 → 开发者模式”里开启开发者模式。这里要注意开启开发者模式通常需要注册统信开发者账号机器需要联网验证身份验证通过后系统会重启。另外开发者模式可能会清空用户数据操作之前务必做好备份。有一个容易被忽略的坑部分UOS版本开启开发者模式之后重启时又会提示“是否校验系统完整性”。如果你选择了校验之前对系统做的所有修改包括我们刚编译好的内核模块都会在重启后消失。实际情况中我第一次就栽在这个坑里编译加载好了binder满心欢喜重启结果一切归零。解决办法是重启时一直不选校验保持系统处于开发者模式可写状态。2.2 安装编译工具链和Anbox源码依赖这里默认你已经把开发者模式开好了并且能以root权限操作终端。打开终端先更新系统源sudo apt update sudo apt upgrade -y然后安装编译内核模块和Anbox本体所需的依赖sudo apt install -y build-essential git cmake pkg-config linux-headers-$(uname -r) dkms sudo apt install -y libgl1-mesa-dev libgles2-mesa-dev libdrm-dev libsystemd-dev libsdl2-dev sudo apt install -y lxc liblxc-1 liblxc-dev libboost-filesystem-dev libboost-iostreams-dev libboost-log-dev libboost-program-options-dev libboost-system-dev libgoogle-glog-dev libprotobuf-dev protobuf-compiler libdbus-1-devlinux-headers这个包尤其关键。编译Anbox使用的binder/ashmem模块时必须要和你当前运行的内核版本完全一致。你可以用“uname -r”确认当前内核版本然后检查/usr/src里有没有对应的linux-headers目录。如果apt提示找不到linux-headers包多半是软件源配置不够全建议先把你当前UOS大版本的源例如专业版、社区版对应的镜像源更新完整再执行安装。2.3 准备内核源码与检查当前内核版本如果你只想走“编译模块”的轻量路线有linux-headers就够了不需要完整内核源码。但如果你选择重新编译整个内核需要拉取完整的内核源码树。UOS官方内核有两个大版本路线一个是4.19另一个是5.10或者更高。先用命令看一下uname -r内核源码的获取途径我通常推荐直接去kernel.org下载对应版本的官方tarball。部分UOS版本自己也提供源码包但有时会有补丁差异反而容易编译出跟运行环境不一致的内核。对于Anbox这种需要严格匹配内核特性的场景主线内核的配置反而更可控。wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.xxx.tar.xz tar xf linux-5.10.xxx.tar.xz cd linux-5.10.xxx当然如果你只是想编译anbox-modules暂时可以跳过这步先看第三章。3. 内核编译与Anbox内核模块加载整个Anbox安装流程里最核心、最容易出问题的就是这一章。很多用户卡在Anbox启动阶段究其根本就是binder和ashmem没有正确加载到内核里。3.1 调整内核选项binder、ashmem 和 binder_ipc_32bit在正式编译前我们先理清楚Anbox究竟需要内核提供哪些能力。除了前面反复提到的binder和ashmemx86_64平台上还有一个关键配置叫CONFIG_ANDROID_BINDER_IPC_32BIT。这个选项的意思是允许binder为32位进程提供IPC接口。很多安卓应用仍然是32位库如果不打开这个选项运行时会出现各种诡异的“java.lang.RuntimeException: Parcel”崩溃和权限错误。如果你选择完整编译内核进入内核源码目录后执行配置make x86_64_defconfig make menuconfig在menuconfig界面导航到Device Drivers → Android → [*] Android Binder IPC Driver [*] Android Binder IPC Driver (32-bit compatibility) [*] Android Ashmem这三个选项保存后你可以直接编译安装整个内核。这个过程机器性能好的话也要一小时左右所以如果你只为了Anbox我还是推荐用anbox-modules方式去编译模块而不要重复造轮子编译整颗内核。3.2 两种内核编译路线整颗重编 vs 动态模块先说整颗重编。这种方式优点是一次性把binder、ashmem、BinderFS全都编进内核运行时不需要额外加载模块系统启动就自带这些能力。但缺点非常明显UOS的apt可能会在将来升级内核版本导致你自编译的内核被覆盖同时UOS一些硬件驱动比如特定显卡或网卡是在官方内核之外额外打包的换了自编译内核以后这些驱动往往不能再接着用重装驱动又是无底洞。再说动态模块。anbox-modules仓库里提供了独立的binder和ashmem模块源码编译后生成binder_linux.ko和ashmem_linux.ko通过insmod或者modprobe临时加载。Anbox启动时只要设备节点存在就能正常工作。UOS后续升级内核也不影响这一次已经加载的模块只是重启后可能需要重新加载这可以通过配置开机自动加载模块来解决。我在项目交付中默认推荐动态模块方案它还有个额外的好处出现了问题不至于把整个系统搞挂。Binder驱动属于Android生态特有部分如果配置不当你的UOS桌面可能在开机时直接卡在登录界面这对最终用户来说是不可接受的。动态模块方案我可以随时卸载模块恢复系统。3.3 编译并安装 anbox-modules搞定 /dev/binder 和 /dev/ashmemanbox-modules是Anbox官方维护的内核模块仓库。我们先把代码拿下来git clone https://github.com/anbox/anbox-modules.git cd anbox-modules仓库里有kernel/目录里面包含ashmem.c和binder.c。直接进入该目录编译cd kernel/ make -j$(nproc)编译完成后会生成ashmem_linux.ko和binder_linux.ko。接着把它们复制到内核模块目录sudo cp ashmem_linux.ko /lib/modules/$(uname -r)/kernel/drivers/staging/android/ sudo cp binder_linux.ko /lib/modules/$(uname -r)/kernel/drivers/staging/android/如果目录不存在需要先创建。然后depmod更新模块依赖关系sudo depmod -a加载模块sudo modprobe ashmem_linux sudo modprobe binder_linux注意新版本的anbox-modules在较新内核上编译binder时可能报错例如“multiple definition of binderfs”之类的错误。这是因为内核版本差异导致binderfs代码冲突。稳妥办法是把ashmem.c和binder.c编译成独立模块同时确认你的内核头文件版本和运行版本一致。加载成功之后检查设备节点ls -l /dev/binder /dev/ashmem正常输出类似于crw------- 1 root root 10, 58 Jun 20 10:23 /dev/binder crw------- 1 root root 10, 59 Jun 20 10:23 /dev/ashmem如果看不到可以手动创建设备节点或者排查dmesg。手动创建的方式如下sudo mknod /dev/binder c 10 58 sudo mknod /dev/ashmem c 10 59这里的主设备号和次设备号要和binder、ashmem在内核中注册的编号一致实际值以dmesg输出的为准。为了确保重启后模块自动加载建议在/etc/modules-load.d/anbox.conf里写入两行ashmem_linux binder_linux以上都做完后就可以进行Anbox本体的安装。4. 安装Anbox并完成基础配置4.1 用snap安装Anbox还是从源码编译Anbox官方推荐用snap方式安装snap是Ubuntu系常见的包管理器UOS虽然基于Debian但本身默认不带snapd。如果你想走官方捷径先在终端安装snapdsudo apt install snapd sudo systemctl enable --now snapd.socket snap install anbox这里有个很大的坑UOS的版本和snapd的兼容性问题会导致Anbox安装后无法启动。我自己在UOS专业版上试过几次snap方式拉下来的Anbox运行时会因为AppArmor的profile设置问题容器进程直接被系统拦截。排查起来非常繁琐很多朋友在这里就放弃了。我后来采用的是从源码编译安装的方式整个过程虽然要编译不少东西但胜在可控。先把Anbox源码拉下来git clone https://github.com/anbox/anbox.git cd anbox mkdir build cd build cmake ../ make -j$(nproc)Anbox的可执行文件会有几个一个是anbox本体一个是anbox-container-manager负责拉起LXC容器另一个是anbox-session-manager负责跟Wayland或X11图形环境集成。编译成功后把可执行文件放到/usr/local/bin下面之后手动管理启动流程。编译过程中如果报缺少依赖仔细看cmake提示缺什么就用apt装什么。我在UOS上经常碰到libprotobuf版本太老导致编译失败这种情况可以用官方提供的libprotobuf开发包替换或者干脆去源码里改CMakeLists让它接受旧版本。4.2 手工启动 container-manager 和 session-manager无论是snap管理还是源码编译底层功能分成两个重要进程。先启动容器管理器它需要有root权限sudo anbox-container-manager --daemon容器管理器会检测/dev/binder和/dev/ashmem是否存在如果模块没加载成功这里会直接报错退出。看到类似“failed to initialize binder”或“Could not open /dev/binder”的日志就回头检查上一章。容器管理器启动正常后再启动会话管理器。这个进程需要以普通用户身份跑不要用sudoanbox session-manager启动成功后通常会在当前桌面环境弹出Android桌面窗口初始时可能是一个空白桌面有任务栏和系统设置图标。如果没有窗口查看日志确认是不是没有构建LXC rootfs。Anbox官方会给snap包附带Android rootfs镜像但源码编译方式需要我们自己准备rootfs。最简单方式是下载Anbox官方提供的android rootfs镜像通常叫“anbox-system.img”或者“android_rootfs.img”。讲一个细节必须把rootfs放到Anbox默认查找的路径一般是/var/lib/anbox/android-rootfs/。sudo mkdir -p /var/lib/anbox/android-rootfs sudo cp android_rootfs.img /var/lib/anbox/android-rootfs/然后还需要准备容器配置文件Anbox源码仓库的“anbox-container”目录下有示例LXC配置复制到/etc/anbox即可sudo mkdir -p /etc/anbox sudo cp /path/to/anbox/container/lxc/*.conf /etc/anbox/4.3 验证Anbox运行状态adb devices 与图面显示Anbox内部自带ADB监听默认监听本地5555端口。我们在宿主机上装一个Android开发工具包或者直接用adb连接adb connect localhost:5555 adb devices正常会返回一个设备状态为device。如果显示offline多半是binder通信出了问题或者Android容器内部进程没起来。如果adb能连上但桌面没有图形窗口检查两个关键配置一是图形环境Anbox对X11的支持比Wayland成熟如果你在UOS默认的Wayland环境下跑很可能会黑屏或者弹不出窗口建议切到X11会话再试二是显卡驱动Anbox使用的OpenGL库需要宿主机提供GLES支持部分集成显卡环境需要安装mesa-utils。当adb连上、窗口也弹出来Anbox基本就算跑起来了。不过这时候还不算完因为安卓App还没有装进去。5. 安卓APP安装全流程与常见问题排查5.1 给Anbox装APKadb install 实操Anbox自带的Android界面非常简陋没有应用商店入口要安装应用最直接的方式就是ADB。拿到APK文件后执行adb install -r your_app.apk-r参数表示允许覆盖安装在多次调试时很有用。安装成功后Android桌面上会出现对应图标点击就可以运行。如果你要批量安装多个APK可以先“adb push”到Android容器内部再逐个安装adb push *.apk /data/local/tmp/ adb shell pm install /data/local/tmp/your_app.apk这种方式在测试阶段比逐个“adb install”要省时间。还有一个我常用的技巧通过Anbox的Android系统里设置代理或者共享目录。你可以在Android容器内开启“设置 → 存储”有时候看不太到宿主机文件。真正好用的是用“adb reverse”把安卓容器里的端口映射到宿主机这样可以方便App调试和访问本地服务。注意有些政企应用会用“移动设备管理”或者“安全加固”策略检查系统环境在Anbox容器里跑会因为设备指纹、SELinux状态不符直接被拒绝运行。这类问题不是安装不够而是运行环境无法通过校验只能退居到兼容市场里找“白名单”版本。5.2 高频率坑ARM应用、签名冲突、AppArmor拦截这一节内容我几乎每次演示都会被问到列成表格最直观。问题现象根因分析解决建议安装提示“Failure [INSTALL_FAILED_NO_MATCHING_ABIS]”APK只包含ARM架构CPU指令而Anbox是x86_64容器安装libhoudini ARM转译层或者找x86/x86_64版本的APK安装提示“INSTALL_FAILED_UPDATE_INCOMPATIBLE”或“签名不一致”设备里已存在同包名但签名不同的应用先卸载原应用再执行安装点击图标启动后白屏/闪退应用要求Android相机、传感器等硬件能力Anbox未提供查看logcat日志只选择不支持该特性的应用或用Waydroid补位App启动后窗口没有渲染只有绿色/黑色画面图形环境或GPU驱动异常切换X11会话升级mesa驱动或启动session-manager加“--software”渲染安装不报错但打开应用后进程被杀AppArmor安全策略拦截检查/var/log/syslog放通anbox相关profile或调整AppArmor状态ARM转译是最大痛点。目前Anbox官方支持libhoudini的旧版本集成你可以搜索“libhoudini anbox”查社区打包好的镜像。简而言之把libhoudini的so库放到Anbox的/system/lib64目录重启Android容器ARM应用就能运行。前提是Anbox的Android版本不是太高太新的Android版本对转译层兼容性很差。签名冲突的问题在测试重复安装时特别常见。比如你上午装了一个企业签名的APK下午用另一个签名文件构建的新包想覆盖安装就会被系统拒绝。此时直接执行adb uninstall com.example.yourapp adb install yourapp.apk干净利落。如果安装过程中出现“Failure [INSTALL_FAILED_USER_RESTRICTED]”这多半跟多用户或应用安装权限有关。在Anbox的adb shell里执行“pm list users”看看有没有僵尸用户删除多余用户往往能解决问题。5.3 官方渠道与第三方商店安卓应用从哪来Anbox里没有应用商店用户怨气很大。我一个项目里直接把常用的几款办公应用预先用adb装好了再让业务人员切换使用。如果你希望给最终用户一个更友好的方式可以考虑在Anbox的Android环境里安装一个第三方应用市场APK比如APKPure、Aurora Store的x86版本。装市场APK的时候同样要挑x86_64版本否则装了打不开这就是你已经在前面ABI问题上白交了学费的原因。应用市场装好后用户就可以在Android窗口里自行搜索安装应用了体验上接近手机。不过也要预防不要装来路不明的APP。政企内网环境往往不允许直接连外网下载APK更稳妥的做法是采购或统一下发签名一致的内部App用adb静默安装到每台终端。6. 实操心得与进阶建议6.1 性能调优内存、CPU、GPU渲染Anbox作为容器方案性能损耗比虚拟化小很多但还是有一些调优空间。首先看内存。Android容器默认分配的内存上限很保守如果你机器内存充足比如是16G或者32G可以把Anbox容器配置里的内存限制调高。容器配置文件位于/etc/anbox/container-manager-config/config.toml或对应的lxc配置里找到memory.limit字段改成你想要的数值例如6GB或8GB。其次是CPU核数。Anbox尽量使用宿主机所有CPU核心但多核Android系统里有些任务会不合理地产生调度开销。如果你的应用主要跑单线程业务适当降低容器内可用CPU核数反而会提升稳定性。最后是图形渲染。Linux桌面环境下Anbox图形性能取决于OpenGL ES实现。UOS默认可能调用Mesa llvmpipe软件渲染性能很低播放视频都能卡成PPT。如果机器有NVIDIA或AMD独显务必安装官方驱动如果是Intel处理器就用mesa-utils确认i965/iris驱动被正确加载。太老的内核配太新的Mesa也可能出现兼容问题这时候只能退回软件渲染。6.2 后续升级与维护注意事项我特别提醒一下UOS系统更新和Anbox是天然的对立关系。每次UOS升级内核anbox-modules必须重新编译Anbox本体如果是由源码编译的升级系统库之后也需要留意动态链接库的兼容性。建议项目落地的机器上将其Anbox相关软件包标记为不自动升级避免一次系统更新把所有组件打散。同时写一个简单的开机自检脚本检测/dev/binder是否存在不存在则自动modprobe并重启anbox-container-manager服务。这类运维细节比起跑通一次更重要不然终端用户在某个平凡早晨打开电脑发现安卓应用全打不开你作为交付方就会被追责。6.3 如果Anbox不够用你还可以试试Waydroid虽然这篇标题是Anbox但我还是想补充一句如果Anbox的性能真的满足不了你的业务那你可以看看Waydroid。它是目前Linux上跑Android容器更现代的方案在Wayland会话下表现优秀对binder实现支持更完善。它最大的问题是你必须克服UOS默认会话和内核特性的适配过程同样不轻松。实际上我在后续项目里已经开始给客户做Waydroid替代方案一台老旧的国产主机上跑Waydroid再装三个定制APK流畅度明显好过Anbox。但Waydroid在UOS上的步骤还比较零散等我把坑都填平了再写一篇更详细的对比指南。最后分享一个小经验这类Linux容器跑安卓工具千万不要试图在UOS上折腾大型游戏或地图软件。它的价值永远是填补“这台机器必须用某个安卓内部业务软件”的特殊需求而不是第二部手机。想清楚这一点你后面投入的时间都会变得非常值。
返回列表