简介:这份资源聚焦 Android 12 系统设置中以太网静态 IP 的配置实现,面向系统开发人员、框架层源码研究者以及需要定制有线网络行为的工程师。资源包内含 8 个文件,其中 6 个 Java 源码文件覆盖以太网配置存储、连接状态追踪、网络工厂与服务接口等核心模块,另附 2 个 txt 文档,分别提供静态 IP 配置指引与相关源码的 Git 提交记录,便于对照代码梳理改动脉络。压缩包整体仅 26KB,轻量便携,适合快速分析 Android 12 以太网子系统的工作机制。当前已有 422 人学习下载,对想理解 EthernetConfigStore 与 EthernetTracker 协作逻辑、掌握静态 IP 配置接口的开发者具有直接参考价值。通过阅读源码与文档,可清晰了解系统如何手动指定 IP、子网掩码、网关和 DNS,并追踪网络状态变化时的处理流程,为后续二次开发或问题排查提供实用依据。
1. 一条网线插上毫无反应:Android 12 设置以太网静态 IP,卡点到底在哪里
把一台 Android TV 盒子接到只有网线、没有 DHCP 的实验室网络,开机后网络图标一直转,浏览器完全不通;想切到以太网静态 IP,设置里却连“以太网”这一项都找不到。用电脑上熟悉的ifconfig去敲,eth0 明明亮着,可重启一次又回到 169.254 开头的自动地址。这不是设备坏了,而是 Android 12 的以太网配置根本没为“随手改 IP”设计:设置入口常被 OEM 隐藏,网卡归系统框架接管,命令行改完不持久。这篇文章要解决的是“Android 12 设置以太网静态IP”这条完整链路:界面怎么进、参数怎么填、命令怎么敲、重启怎么保留、失败看哪里。适合搭 Android 测试环境、做 Android TV 和车载以太网集成、或者拿开发板做网口产品的人。
2. 从三层网络栈看 Android 12 以太网静态 IP:为什么改个 IP 要牵动设置、框架和内核
2.1 三层结构:设置界面、EthernetService 框架与 netd
在 Android 12 上把网线插进设备,行为跟你熟悉的 Rocky Linux 或 Ubuntu 设置静态 IP 完全不同。服务器上改完配置文件,NetworkManager 就认账;Android 没有/etc/network/interfaces,也不读 netplan。以太网从插线到拿到地址,至少要经过三层:最上层是设置应用,中间是系统服务 EthernetService,最底层是 netd 与内核网络栈。
设置应用把你填的“静态 IP”保存成一个 EthernetConfiguration 配置对象,交给 EthernetService;EthernetService 再把配置翻译成 netd 能执行的命令,让内核接口拿到地址和路由;最后才是ip link set eth0 up这一类动作。这里最关键的一点是:EthernetService 会把这个配置持久化到系统目录,并在每次开机时重新套用。所以一个 IP 该不该丢、重启后还在不在,取决于你有没有真正走到 EthernetService 这一层,而不是命令打得对不对。
做 android framework 开发的人,这层代码通常在EthernetServiceImpl和EthernetTracker里;对拿到成品盒子的普通用户而言,你只需要记住一句话:任何绕过系统服务直接改内核的命令,都可能被框架在下一次网络重估时抹掉。这也是后面几章反复强调“要用设置界面或系统 API 固化”的根本原因。
2.2 谁能看到“以太网”入口:Android TV、车载以太网与 OEM 隐藏
很多人翻遍手机设置找不到以太网,这不奇怪。Android 12 的手机版本往往没有以太网驱动,Settings 里自然不会出现入口;就算有,也经常被 OEM 用代码隐藏。真正会天天用“以太网静态 IP”的,是三类设备:Android TV 盒子、带车载以太网的车机、以及工业平板和测试台架。
在 Android TV 上,入口一般在“设置 - 网络和互联网 - 以太网”;点进已连接的网卡,会出现“IP 设置”下拉框,里面默认是 DHCP,改成“静态”后就能填 IP。注意“以太网”这三个字可能不直接显示,而是藏在网卡详情页里。车载设备受 Android Automotive 的机制影响,入口由厂商决定,很多车型放在“开发者选项 - 网络”里。
OEM 隐藏入口的手法通常有两种:一是让网卡匹配不上config_ethernet_iface_regex,系统干脆认为没有以太网接口;二是在 Settings 的 overlay 里把以太网菜单删掉。如果你拿到的设备被这么处理过,不管怎么找设置都不会有结果,这不是你配置错误,而是产品行为。第三章会给出绕过方式,第四章再讲 overlay 的正确用法。
2.3 四种网络场景决定怎么填静态 IP
静态 IP 不是把四个数字填进去就完事,先搞清楚设备处在什么拓扑里。
第一个场景是同一网段:电脑和盒子接同一个交换机,IP 填 192.168.1.100/24,网关填 192.168.1.1,DNS 填网关或运营商 DNS。这是最常见的实验室拓扑,既能互相访问,也能出公网。第二个场景是跨网段:设备自己住在 192.168.1.0/24,却要访问 10.0.0.0/8,这时网关必须填成能路由到目标网段的三层设备地址,光填 IP 没用。第三个场景是纯局域网隔离:测试环境没有出口,网关和 DNS 留空,只填 IP 和掩码。很多工程师习惯性把网关填上去,反而让系统进入“验证失败”状态。第四个场景是点对点直连:电脑网口直连盒子,用 192.168.1.x/30 这类小掩码,避免多设备抢地址。
为了让你抄参数时有据可依,我把这些场景整理成一张表:
| 场景 | IP 示例 | 掩码 | 网关 | DNS |
|---|---|---|---|---|
| 同一网段 | 192.168.1.100 | 255.255.255.0 | 192.168.1.1 | 114.114.114.114 |
| 跨网段 | 192.168.1.100 | 255.255.255.0 | 10.0.0.1 | 内网 DNS |
| 纯局域网 | 192.168.1.100 | 255.255.255.0 | 留空 | 留空 |
| 点对点 | 192.168.1.1 | 255.255.255.252 | 不填 | 不填 |
掩码决定网段范围,/24 等于 255.255.255.0,/30 等于 255.255.255.252,只容纳两个可用地址。点对点场景里网关通常写在对端设备上,本端不填。很多人在这里翻车,是因为把服务器场景的习惯带到了 Android 上。
2.4 四种配置手段的取舍
| 手段 | 生效范围 | 重启是否保留 | 适用人群 | 主要风险 |
|---|---|---|---|---|
| 设置界面 | 框架层 | 保留 | 终端用户 | 入口可能被 OEM 隐藏 |
| adb 命令临时配置 | 内核层 | 不保留 | 临时调试 | 框架重估时可能被覆盖 |
| EthernetManager 代码 | 框架层 | 保留 | 系统应用集成 | 需要系统权限 |
| overlay 固定配置 | 框架层 | 保留 | 厂商出厂 | 需要重新编译固件 |
设置界面的好处是所见即所得,系统会把配置交给 EthernetService 持久化,适合现场维护。adb 命令临时配置适合快速验证链路通不通,但不适合作为最终交付。EthernetManager 代码适合系统应用或平台应用在运行时动态切换,比如车载场景里根据档位切换网段。overlay 则解决“开机第一次就能用”的问题,但改的是系统映像,没有后悔药,必须留好原厂固件。四年做机顶盒集成的经验告诉我:能走设置界面就先走设置界面,命令行只用来排查,代码只在需要自动化时上。
3. 用设置界面与 adb 命令在 Android 12 上配通静态 IP:最小可复现步骤
3.1 打开以太网设置页:常规入口、Intent 与隐藏界面唤醒
拿到一台新盒子,我做的第一件事不是进设置,而是跑adb shell dumpsys ethernet,看系统到底认没认到网卡。输出里有mIface: eth0、mIpAssignment: STATIC这样的字段,说明入口就在界面顶层;如果这条命令什么都没返回,说明接口还没进入框架,后面怎么填都是空的。
常规入口是“设置 - 网络和互联网 - 以太网”,点击接口名,在详情页里把“IP 设置”从 DHCP 改成“静态”。如果入口被藏,先用 adb 尝试拉起设置页。Android 12 的 AOSP 分支没有公开稳定的以太网设置 action,常见做法是直接指定 Activity 类名:
# 先看当前设备的系统里,有没有名为 Ethernet 的 Activity adb shell pm dump com.android.settings | grep -i ethernet # 如果能拉到通用 action 就最省事 adb shell am start -a android.settings.ETHERNET_SETTINGS # 不行就按类名启动,AOSP 上常见的是下面两个类 adb shell am start -n com.android.settings/.ethernet.EthernetSettings adb shell am start -n com.android.settings/.Settings\$EthernetSettingsActivitypm dump那一步是为了确认类名,OEM 分支常把设置页挪到自有包里,类名完全不同。am start -n后面的组件名必须和系统里实际存在的类一致,否则会报Activity not found,这属于正常现象,换一套类名再试就行。如果你用的不是 adb,而是想在自己 App 里跳转,用 Intent 的setComponent同样可以,但要注意目标 Activity 可能被厂商标记为非导出,普通 App 跳转会被拒绝。
3.2 静态 IP 表单参数怎么填:IP、掩码、网关与 DNS 的推荐值
表单字段通常有四个:IP 地址、子网掩码、网关、DNS。IP 地址不要和网关、其他设备冲突,建议在网段里挑一个靠后的值,比如 192.168.1.100。子网掩码选择 /24,也就是 255.255.255.0,覆盖 254 个可用地址,适合绝大多数实验室和办公网络。
网关要填实际存在的三层设备地址。纯局域网隔离环境里网关可以留空,这一点很多人不习惯,总想填一个“看起来像网关”的地址,结果适得其反。DNS 至少填一个,如果内网有自建 DNS 就填内网地址,没有就填运营商 DNS 或公共 DNS。填完以后系统会立刻把配置推给 EthernetService,你下拉刷新网络状态,应该能在一个呼吸之间看到 IP 生效。
这里有一个容易忽略的参数:Android 12 的“私人 DNS”设置。如果设备开了私人 DNS,而你的内网没有对应服务器,即使静态 IP 配置正确,域名解析也会失败。遇到这种情况先到“设置 - 网络和互联网 - 私人 DNS”里关掉,或者执行adb shell settings put global private_dns_mode off。
3.3 adb 临时配置:用 ip addr 和 ip route 把网口点起来
界面不可用、又要快速验证链路时,才用 adb 临时配。注意“临时”这两个字,这种配置写在内核层,框架重估网络时可能被覆盖。命令如下:
# 需要 userdebug/eng 固件,或者设备已 root;普通零售机大概率执行失败 adb root # 查看当前网口,确认是 eth0、eth1 还是 usb0/rndis0 adb shell ip link show # 给 eth0 添加地址;掩码 /24 等价于 255.255.255.0 adb shell ip addr add 192.168.1.100/24 dev eth0 # 把网口启用 adb shell ip link set eth0 up # 默认路由指向网关 adb shell ip route add default via 192.168.1.1 dev eth0ip addr add是叠加操作,如果网口上已经有地址,想换 IP 需要先ip addr flush dev eth0,否则会同时存在两个地址。ip link set eth0 up必须放在添加地址之后还是之前,其实顺序不影响最终结果,但先add再up更容易判断是哪一步出错。ip route add default只能加一条默认路由,如果已经存在,系统会报 “file exists”,这时用ip route replace覆盖更稳妥。
这套命令在 Android 12 的 toolbox 环境里基本可用,ifconfig那个老命令也能用,但它看不到策略路由,不推荐。配置完成后,同一子网内应该能 ping 通,但要访问其他网段,必须确认网关路由确实写进了内核表。
3.4 配完怎么验证:从 ping 到 dumpsys ethernet
配置完事情只做了一半,验证才是重点。验证顺序有讲究,先验证链路,再验证网关,再验证 DNS,最后验证框架状态:
# 第一步:本机地址有没有真正落到网口上 adb shell ip addr show eth0 # 第二步:同一网段的对端能不能通 adb shell ping -c 4 192.168.1.10 # 第三步:网关是否可达 adb shell ping -c 4 192.168.1.1 # 第四步:框架层是否认识这个网络 adb shell dumpsys ethernet adb shell dumpsys connectivity | grep -A 20 -i ethernetip addr show eth0输出里要能看到192.168.1.100/24和state UP,如果网口还在DOWN,检查网线、接口名对不对。dumpsys ethernet会显示框架当前持有的配置,重点看mIpAssignment字段,是STATIC还是DHCP。如果界面里明明是静态 IP,dumpsys却显示 DHCP,说明设置没有真正提交,回到设置页重新保存一次。这一连串验证做完,才叫“配通”。
4. 让静态 IP 重启不丢:EthernetManager 代码与 overlay 固化的两条路
4.1 先拿到真实接口名:别把 eth0 写死在脚本里
很多脚本习惯把接口写成eth0,但 Android 12 设备千奇百怪:原生机顶盒是eth0,插上 USB 网卡可能变eth1,用 USB 共享网络时是usb0或rndis0。拿到设备先别猜,用命令看:
adb shell ip -o link show输出里每一行就是一个接口,eth0、eth1、rndis0、wlan0都会列出来。要更准确,可以抓系统已经识别到的以太网接口:
adb shell cmd ethernet help 2>/dev/null || adb shell dumpsys ethernet | grep -i iface有些固件没有cmd ethernet命令,所以第二句里的dumpsys ethernet是兜底。接口名的作用是给后面的配置脚本和代码定位网卡,写死eth0会带来一个隐蔽问题:设备有时候先枚举无线网卡,再枚举以太网,eth0和eth1会在重启后互换。正确做法是根据 MAC 地址或驱动名字绑定接口,而不是靠序号。
4.2 系统应用里用 EthernetManager 设置静态 IP:Kotlin 片段与权限边界
要写代码固化静态 IP,最靠谱的是走 EthernetManager。这块接口属于系统 API,普通 App 无法调用,只有平台签名或系统权限应用才能碰。下面这个 Kotlin 片段以 AOSP Android 12 的常见实现为参考,不同 OEM 分支符号会略有差异:
val ethManager = applicationContext .getSystemService(Context.ETHERNET_SERVICE) as EthernetManager // 拿到系统识别到的第一个以太网接口;实际产品里建议循环匹配 val iface = ethManager.availableInterfaces.firstOrNull() ?: return val staticIp = StaticIpConfiguration.Builder() .setIpAddress(InetAddress.getByName("192.168.1.100"), 24) .setGateway(InetAddress.getByName("192.168.1.1")) .addDnsServer(InetAddress.getByName("192.168.1.1")) .build() // Android 12 的 EthernetManager 暴露给系统应用的配置对象是 EthernetConfiguration val config = EthernetConfiguration() config.ipConfiguration = IpConfiguration.Builder() .setStaticIpConfiguration(staticIp) .build() // setConfiguration 的重载在不同版本上有差异,以设备源码为准 ethManager.setConfiguration(iface, config)这段代码说明三件事:第一,接口不能写死,通过availableInterfaces动态拿最安全。第二,StaticIpConfiguration.Builder需要在build()之前把 IP、掩码、网关、DNS 四个信息全部组装完,漏掉网关或 DNS,配置提交后行为会变得难以预测。第三,EthernetConfiguration和setConfiguration是系统 API,放进 Android Studio 的普通工程编译可能直接报错,常见做法是把代码编进系统应用,或者用平台签名。
还要注意权限,工程里必须声明android.permission.NETWORK_SETTINGS或对应系统权限,并且应用要放到/system/priv-app或/system/app目录。没有权限时,调用会抛SecurityException,现象是应用直接崩溃,抓 logcat 看到Permission Denial就能定位。
4.3 overlay 能做什么、不能做什么:识别网卡与默认配置的关系
很多工程师误以为在 overlay 里放一个 IP 地址,开机就会自动配置。实际上 AOSP 的 overlay 机制并不直接承载“IP 地址”,它只能修改系统资源,比如告诉框架“哪些接口算以太网”。最常见的是这个资源:
<!-- device/xxxx/overlay/frameworks/base/core/res/res/values/config.xml --> <resources> <!-- 让框架把 eth5 也识别成以太网口,正则里的双斜杠是 XML 转义需求 --> <string translatable="false" name="config_ethernet_iface_regex"> "eth\\d|rndis\\d|usb\\d" </string> </resources>config_ethernet_iface_regex的作用是让 EthernetService 扫描内核接口时,把匹配到的名字归入以太网管理。很多盒子的无线网卡叫wlan0,以太网叫eth0,默认正则只认eth\d,一旦设备的网卡被驱动命名为enx0050b6xxxxxx,系统就不认它是以太网,设置页里自然什么都看不到。
overlay 不能直接写死静态 IP,原因是 IP 配置属于运行时状态,归 EthernetService 管理。如果一定要出厂就是静态 IP,常见做法是在 EthernetTracker 启动时注入一个默认 EthernetConfiguration,或者在设备首次开机时用系统应用调一次setConfiguration。这个动作需要重新编译固件,风险高,改之前务必确认原厂固件可以完整刷回。换句话说,overlay 是给网卡“上户口”的,不是给网卡“发工资”的。
4.4 框架层设置后的网络评分问题:静态 IP 也可能被判为“无网”
用代码或设置界面把静态 IP 配好之后,可能会遇到一个怪相:IP 明明是对的,网关也能 ping 通,状态栏却显示“无法访问互联网”,部分应用直接拒绝发包。原因在 Android 12 的网络评分机制。
Android 认为一个网络“可用”,要能完成一次网络验证,通常是访问 Google 的连通性检测服务器。实验室局域网没有外网,验证失败,网络分变低,系统就把它标记为“未验证”。解决方式很多,最简单的是关闭网络验证或强制接受:
# 关闭 captive portal 检测,0 表示永不检测 adb shell settings put global captive_portal_mode 0 # 如果已经检测过且被判无网,清掉状态让系统重新评估 adb shell cmd connectivity resetcaptive_portal_mode的取值 0 是关闭,1 是默认开启,2 是仅主动上网时检测。改完以后,系统不会再因为外网不通而把局域网网络标记成“不可用”。需要注意,这是全局设置,会影响该设备上所有网络,不只是以太网。在生产固件里,低速产测工具往往会主动关掉这个检测;正式交付给客户的固件,最好评估后再决定关不关。
5. Android 12 以太网静态 IP 的五个常见坑:现象、原因与解决办法
5.1 重启后配置丢失:ifconfig 制造的所有“假成功”
现象:用ip addr add或ifconfig配上静态 IP,当时一切都正常,也能 ping 通,但只要重启,地址立刻消失,或者回到 DHCP 获取的地址。
原因:命令行配置作用于内核,而 Android 的 EthernetService 会在网络重新评估时恢复自己保存的配置。如果你没有把配置写进框架层,重启后框架按默认 DHCP 重新配置网卡,之前的地址自然被清掉。
解决:不要指望命令行能固化。要永久生效,进“设置 - 网络和互联网 - 以太网”改静态,或者用系统 App 调EthernetManager.setConfiguration。改完以后,用dumpsys ethernet确认mIpAssignment是STATIC,再重启复验一次。
5.2 “以太网”入口凭空消失:接口未被框架识别
现象:网线插上,指示灯亮,adb shell ip link能看到eth0,但系统设置里完全没有“以太网”菜单,怎么滑动都没有。
原因:框架的config_ethernet_iface_regex没有匹配到eth0。有些设备网卡名字带厂商前缀,比如enx...,默认正则以eth\d开头,认不出这种接口。也可能是 OEM 在 Settings overlay 里把菜单删了。
解决:先用dumpsys ethernet确认框架是否看到网卡。如果dumpsys里完全没有接口信息,说明是正则匹配问题,要改 overlay 里的config_ethernet_iface_regex。如果dumpsys有接口,但设置页看不见,那就是纯 UI 隐藏,用第三章的 Intent 类名方式强拉设置页。
5.3 静态 IP 配好了,应用流量却仍走 Wi-Fi
现象:以太网配好了静态 IP,电脑能 ping 通盒子,但盒子上的应用打开网页、视频,走的还是 Wi-Fi,甚至切到 Wi-Fi 通路后以太网也没受影响,像是两个网络在平行运行。
原因:Android 12 的多网络策略里,同时存在 Wi-Fi、以太网两个网络时,系统选择由网络分和验证状态共同决定。如果以太网被判成“未验证”,而 Wi-Fi 验证通过,应用默认流量会优先走 Wi-Fi。另外,Wi-Fi 和以太网的默认网关如果相同,策略路由也可能随机选一个。
解决:不依赖 Wi-Fi 时,直接关掉 Wi-Fi,让以太网成为唯一网络。如果要整机出网走以太网,在框架层调整NetworkCapabilities或提升以太网网络分。临时验证时也可以关闭网络验证,见 4.4 的设置项。
5.4 网关能 ping 通,域名解析却一片空白
现象:IP、网关、DNS 都填了,网关ping通,但ping 内网域名或打开网页时提示无法解析,设备连内网服务器都找不到。
原因:DNS 地址没有真正传递到系统 resolver。用命令行临时配置时,DNS 是写在resolv.conf或参数里的,但 Android 的 resolver 由 netd 管理,命令行直改不经过它,白白填了。另一种原因是私人 DNS 设置覆盖了静态 DNS。
解决:DNS 必须通过框架配置。回到设置页的静态 IP 表单,把 DNS 填上,或通过EthernetManager代码里的StaticIpConfiguration.Builder.addDnsServer传入。同时执行:
adb shell settings put global private_dns_mode off这条命令把“私人 DNS”关掉,避免系统优先使用私人 DNS 而忽略手动 DNS。
5.5 IP 静态写在框里,系统仍提示“无法访问互联网”
现象:设置页里是静态 IP,地址正确,网关也能 ping 通,但状态栏显示“无互联网”,部分应用表现成离线,甚至视频 App 拒绝播放。
原因:这是 4.4 里说的网络验证问题,静态 IP 配在隔离局域网里,系统去访问验证服务器失败,于是把网络标记为“无网”。应用层会观察NetworkCapabilities的NET_CAPABILITY_VALIDATED,标记为 false 就认为网络不可用。
解决:执行adb shell settings put global captive_portal_mode 0,或者更细粒度一点,只改captive_portal_mode为 2,让系统在主动上网时才做验证。改完以后,状态栏应该会在一两分钟内恢复“已连接”,应用也能正常发包。这个问题几乎每个搞产测的工程师都会遇到,属于 Android 系统行为,不是配置错误。
6. 一个快照命令把配置钉死:dumpsys、tcpdump 与重启验收
6.1 抓取完整的网络配置快照
配好静态 IP,别急着收工。把所有关键状态一次性抓下来存档,后续出问题才有对比依据:
adb shell dumpsys ethernet > eth_dump.txt adb shell dumpsys connectivity | grep -i -A 20 ethernet >> eth_dump.txt adb shell ip addr show >> eth_dump.txt adb shell ip route show table all >> eth_dump.txt adb shell settings put global captive_portal_mode 0 adb shell settings get global captive_portal_mode这份快照里,dumpsys ethernet看框架是否真的保存了静态配置,dumpsys connectivity看网络评分和验证状态,ip route show table all看策略路由有没有把默认流量指向正确网卡。保存成文本后放到测试记录里,这就是你这次配置的后悔药。
6.2 我现在的固化与验收流程
这些年被重启丢配置、Wi-Fi 抢流量、DNS 不生效这几件事反复教育过之后,我现在每配一台设备都固定走五步:第一步,ip link确定接口名,不猜 eth0。第二步,能进设置页就走设置页,进不去就用系统应用方式或 class 名拉页面。第三步,配置完立刻dumpsys ethernet,确认mIpAssignment是STATIC。第四步,执行captive_portal_mode 0,避免内网网络被判成无网。第五步,重启一次,再跑一遍快照命令,看配置还在不在。
如果遇到重启丢配置,我不会再去补一次ip addr add,而是直接检查是不是没走框架;如果遇到网关通但域名不通,我先关私人 DNS,再检查静态 DNS 到底有没有传进 netd。网络验证问题则一律先看dumpsys connectivity里的验证标记,而不是盲目改 IP。最后一个小建议:把 eth_dump.txt 命名成带有日期和盒子序列号的文件,一个月后你会发现排查效率提升一大截。希望帮到你。
本文还有配套的精品资源,点击获取