前段时间“不务正业”,研究了 Android 系统同时上内外网的方案。

业务场景需求:一个设备包含两个主板,两主板之间需实现内部通信,其中一个主板需与外部通信(通过以太网或 WIFI 连接本地局域网或互联网)。

即实现如下两种模式的网络拓扑:

场景一:两主板直接通过网线互联,其中一个主板通过 WIFI 与本地局域网及互联网通信

WIFI上外网场景

场景二:两主板通过内置路由器互联,其中一个主板通过以太网与互联网通信,通过 WIFI 与本地局域网通信

WIFI上外网场景

一开始,借鉴网上的方案 —— 大多是修改源码支持多网络共存,修改路由表支持同时上内外网。经过多次验证,发现单纯修改路由表虽然当时能解决上网问题,但是一旦网络出现变化,如关闭打开其中一个网络,或断开重新连接一个网络,路由表便会发生变化,导致无法按预期访问网络,表现不太稳定。经过一段时间的自行探索,及大量的测试验证,目前实现的方案表现较为稳定,现记录如下。

说明:本文基于 Android 5.1,版本较低,对于高版本可能网络实现方式不同,不保证适配。

一. 网络配置使用

1. 功能特性

  1. 提供了双网卡(WIFI 与以太网)同时上网支持,满足前文提到的两种网络拓扑需求
  2. 支持默认网络的切换(默认网络即上外网的网络)

2. 默认网络切换

  1. 系统默认为WIFI上外网,以太网上内网的模式(该模式不需要额外内置路由器)
  2. 修改文件 /data/data/com.android.launcher3/default_network.txt 内容为 WIFI,重启系统,将切换为WIFI上外网,以太网上内网模式(APP可提供配置界面进行配置)
  3. 修改文件 /data/data/com.android.launcher3/default_network.txt 内容为 Ethernet,重启系统,将切换为以太网上外网,WIFI上内网模式(APP可提供配置界面进行配置)
  4. 除WIFI,Ethernet外,暂不支持其它值

二. 具体实现说明

1. 支持多网络共存

Android 系统与 Windows 系统不太一样,针对移动场景下降低功耗的需求做了一些优化,在网络部分设计了评分机制(Android 5.1中 WIFI:60,以太网:150),当有高分网络可用时,就会释放低分网络的资源。所以默认情况下只有一个网络可用,可以通过修改源码解决多网络共存问题。

frameworks/base/services/core/java/com/android/server/ConnectivityService.java
Android多网络共存

2. 修改路由表

Android 系统的路由表规则较为复杂,路由表由系统自动生成,根据网络类型不同自动生成对应路由表,如以太网路由表,WIFI 路由表等。所以,即使以太网和 WIFI 可以共存,但是系统默认还是优先走优先级更高的路由表规则,我们需要修改优先级更高的路由表来支持内外网的同时访问。

但是路由表是动态生成的,每次网络变化路由表也会随着动态变化,比如网络断开,之前存在的路由表项会默认被清掉,重连之后它不会自动添加我们之前添加的规则,需要再次手动添加。这时我们可以通过监测网络状态变化事件,动态修改路由表,但是该实现较为复杂,且稳定性也有待验证。

Android 系统中的 main 路由表包含了各个网络路由表的路由规则(除了默认路由),并且能根据网络状态变化动态变化,满足我们的需要,所以我们只需要修改一下 main 路由表的优先级,把它调到最高就可以实现两个网段的访问。

查看 main 路由表内容:

1
2
3
~]# ip route list table main
10.168.200.0/24 dev eth0 proto kernel scope link src 10.168.200.136
192.168.150.0/24 dev wlan0 proto kernel scope link src 192.168.150.230

将 main 路由表的优先级设置为9000:

1
busybox ip rule add from all lookup main pref 9000

3. 制定脚本,开机执行修改路由表

以上命令在系统重启后就失效了,可以通过开机执行命令来解决。

3.1 创建开机启动脚本

创建启动脚本文件: device/rockchip/rk3288/mainroute.sh,启动脚本内容如下:

1
2
3
#!/system/bin/sh
echo "modify route table"
busybox ip rule add from all lookup main pref 9000

3.2 copy 启动脚本到 android 镜像

修改 device/rockchip/rk3288/device.mk, 增加如下语句:

1
2
3
#mainroute
PRODUCT_COPY_FILES += \
device/rockchip/rk3288/mainroute.sh:system/bin/mainroute.sh

3.3 增加启动语句

修改 device/rockchip/common/init.rk30board.rc ,添加如下内容

1
2
3
4
5
6
7
8
service mainroute /system/bin/mainroute.sh
class main
user root
group root
disabled
oneshot
on property:sys.boot_completed=1
start mainroute

4. 修改切换默认网络规则

main 路由表中只有 IP 网段的路由规则,如果需要访问外网,则需要将默认路由指定为能上外网的网卡,经过查看日志及验证,Android 5.1中默认路由由默认网络确定,即如果WIFI 为默认网络,则默认路由走 WIFI。而默认网络的切换是根据网络评分来的,参考源码如下:

frameworks/base/services/core/java/com/android/server/ConnectivityService.java
Android默认网络判断

既然我们只需要支持固定的某个网卡上内网、某个网卡上外网的场景,则可以将其直接写定,而不根据网络评分(会动态变化)来评判。只需要将上外网的网络直接设定为默认网络即可达到目的,修改源码如下:

frameworks/base/services/core/java/com/android/server/ConnectivityService.java
Android默认网络判断

ConnectivityService 初始化时从文件 /data/data/com.android.launcher3/default_network.txt 读取默认网络配置,读取失败,则默认网络设置为 WIFI。
然后将默认网络切换的判断条件由评分高低改为是否为设置的默认网络
Android默认网络判断

三. 相关命令参考

  1. 列出路由策略(路由表列表)
1
ip rule list
  1. 打印路由表 eth0 的路由规则
1
ip route list table eth0
  1. 将main路由的表的优先级设置为9000
1
busybox ip rule add from all lookup main pref 9000
  1. 查看某个IP访问的路由
1
ip route get 47.110.189.28
  1. 查看DNS
1
getprop |grep dns
  1. 添加dns
1
2
setprop net.eth0.dns1 8.8.8.8
setprop net.eth0.dns2 114.114.114.114
  1. 查询 eth0 配置
1
getprop | grep eth0
  1. 往wlan0路由表中添加一条路由规则”10.0.200.0/24 dev eth0 proto static scope link”
1
ip route add 10.0.200.0/24 dev eth0 proto static scope link table wlan0
  1. 获取内核日志
1
adb shell dmesg > dmesg.txt
  1. 获取logcat日志
1
adb logcat -v time > E:/android/logcat.txt

四. 总结

本文介绍了一种 Android 系统下同时支持访问内外网的方案,经过测试验证,表现较为稳定,能满足文中提到的两种场景需求。但正如文章开头所说,研究 Android 属于“不务正业”,理解偏颇之处在所难免,且仅在 Android 5.1版本下验证过,仅供参考。

评论