网络支持技术实现
选择lxc容器
fde使用lxc容器,而lxc有多种网络虚拟类型,veth是waydroid原生使用的网络虚拟类型,他会在host和container(安卓)两边都创建虚拟以太网设备。
我们的目的是容器直接共享host的网络,而lxc的none网络虚拟类型刚好就是这种需求,关于lxc的网络配置可以参考注释①。
下面关于代码的修改都是基于问题为导向来调试,目前对lxc和安卓及linux关于网络的现有实现认知还比较有限,所做的修改可能在整个系统层面并不合理。
要使用lxc的none网络虚拟类型,host和container都要做一定修改。
host端
涉及 waydroid/data/configs/config_3
和 waydroid/data/scripts/waydroid-net.sh
:
config_3 配置文件对应目前使用的 LXC 版本,而其他 config_N 配置文件是兼容很老的 LXC 版本,并且不再进行修改。在 config_3 中,只需将 LXC 的 veth 网络虚拟类型修改为 none (lxc.net.0.type = none)。LXC 在内部会根据该配置来设置网络。
这里只要修改 lxc 的 veth 网络虚拟类型为 none(lxc.net.0.type = none),lcx 内部会根据该配置去设置网络。
waydroid-net.sh
这个脚本文件主要作用是在 host 端创建一个网桥设备,设置各种地址信息和路由表以及启动 dns 相关的程序 dnsmasq;使用 none 类型时,
waydroid-net.sh
基本不用再干什么事情,仅保留了原先的创建和删除标志文件逻辑,用于标识网络的 start 和 stop,并额外添加了防止容器对网络的操作导致的网络问题影响到 host。而增加了备份和设置网络的逻辑(安卓端已经屏蔽了对网络接口的设置,现在应该不会出现导致 host 网络不重启无法恢复的问题)。
container端
当涉及安卓端适配 LXC 的 none 网络类型时,您只需修改 config_ethernet_iface_regex 配置,该配置用于表示需要使用的以太网名称的正则表达式匹配。安卓原生默认为 eth\\d,即匹配以 eth0、eth1 等命名的接口。
根据已知的使用场景,我们需要支持以太网、USB WiFi 和手机 USB 共享网络三种类型的接口名匹配。参考 Linux 对网络接口的命名规则,正则表达式可以设置为 (en.+|wl.+|usb\\d)。基于已经做出的修改,目前已支持以太网和手机 USB 共享网络,这两种方式都支持 DHCP。
对于需要静态 IP 配置的场景,还需要进行其他相关的修改。frameworks/opt/net/ethernet 仓库实现了以太网相关功能。在使用某个网络接口时,它会读取 /data/misc/ethernet/ipconfig.txt 文件中的静态网络配置信息。因此,需要增加将静态网络配置记录在 ipconfig.txt 的逻辑,并实现读取静态网络配置的代码。这部分代码可以直接参考 Redroid 或 Anbox 的代码,它们实现了类似的功能。最后,在 rc 文件中添加程序的执行时机。
具体的代码可以参考 phytium/fde/{init.fde.rc,ipconfigstore}。现在已经可以支持以太网静态 IP 配置的场景。但是,在使用过程中可能会有一个不太好的体验。当通过 SSH 连接到主机时,启动 FDE 会导致网络断开连接,因为安卓启动时会清除网络接口的地址并设置配置,这会导致网络断开一次。
在安卓中,关于网络的大体逻辑如下:安卓框架层对网络执行的相关操作最终会由后台程序 netd 统一处理。netd 监听内核发出的 netlink 事件,并通知框架层。因此,当框架层设置网络配置后,需要等待内核事件的反馈。其他网络模块也会根据这些事件判断是否可以建立 "可用网络环境"(参考注释②)。
在 FDE 启动之前,主机的网络已经可用,实际上不需要再执行针对网络接口的地址清除和配置设置操作。然而,如果仅仅简单屏蔽这些操作,会导致安卓网络生效的延迟问题。通常情况下,在桌面启动后,需要等待2分钟(有时甚至更长时间,例如5分钟以上)才能正常使用网络。
经过调试发现,这是因为其他网络模块需要等待适当的 netlink 事件才能建立 "可用网络环境"。目前还不清楚为什么最终会收到这个 netlink 事件,因为还没有研究过 Linux 的 netlink 机制。为了解决网络生效延迟的问题,找到了一种解决方案:在 packages/modules/NetworkStack 中,通过已知的静态网络配置填充原本应由 netlink 事件传递的信息,以触发建立 "可用网络环境"。
关于 WiFi 的支持:当使用 USB WiFi 时,驱动程序会创建一个以 "wl" 开头的网络接口。然而,原生的 Redroid 并不能正常使用 USB WiFi。参考《深入理解 Android WiFi NFC 和 GPS 卷(邓凡平)》一书中的相关章节,通过加入一些工程的编译、创建必要的目录、关闭 shutdown WiFi、适配非固定的 wlan0 名称等修改,可以实现基本的 WiFi 上网功能。
具体代码修改见:
frameworks/opt/net/wifi
device/phytium/fde
调试过程中针对一些特定的使用场景也做了相应的考虑,假设host在fde启动前网络还未配置好,当使用以太网静态Ip配置时,临时将host网络配置好,按安卓原生的逻辑还是无法使用网络,解决办法是,增加在网络状态改变且为up的情况读静态配置的逻辑;
usb wifi反复插拔会出现网络无法使用的问题,针对手里测试的硬件设备发现是加载wifi驱动时,有个网络接口改名的动作导致安卓接收的netlink事件混乱,无法创建"可用网络环境",现在的解决办法是针对wifi设备,接收到设备移除事件时,依然保留之前的配置;
具体代码修改见:
frameworks/opt/net/ethernet
还可以考虑优化的方向:
wifi设备目前也只是支持了基本的上网功能,一般wifi设备能作为不同的角色实现不同的功能,
驱动层面也会加载不同的模块;各种小概率的使用场景还缺乏探索
所有涉及的代码目录(目前均为network_dev分支):
安卓端:
device/phytium/fde/
frameworks/base/
frameworks/opt/net/ethernet/
frameworks/opt/net/wifi/
packages/modules/NetworkStack/
system/netd/
宿主机端:
waydroid
注释:
①https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html
②"可用网络环境"想表达的意思是网络正常可访问时,各个模块合理的状态