第八板块:Android 网络体系与连接管理 | 第二十二篇:ConnectivityService 与 Netd 网络架构
所属板块:第八板块 --- Android 网络体系与连接管理
前置知识:第二十一篇中的存储体系、Linux 内核网络栈(Netfilter/iptables)、Binder IPC、SELinux 安全上下文
本篇定位 :这是 Android 设备连接世界的神经网络 。如果说应用进程是孤岛,那么 ConnectivityService 就是路由中枢 ,Netd 就是边界网关 。本篇将彻底拆解 Network Stack 的分层架构、ConnectivityService 的网络评分(Network Score)机制 、Netd (Network Daemon) 的命令执行流水线 、策略路由(Policy Routing)与 iptables 防火墙 、VPN 的路由劫持 。我们将深入 Kernel Network Stack 、Native Daemon 与 Framework Service,揭示 Android 如何在复杂的网络环境中(Wi-Fi/蜂窝/以太网/VPN)选择最优路径并确保安全。全程无网络请求代码、无抓包教程,仅保留 Android 网络管理的底层定义与系统级调度规范。
1. 核心结论先行(Thesis Statement)
Android 的网络管理是一个基于评分的动态路由系统。
- ConnectivityService 的本质 :网络仲裁者 。它运行在 System Server 中,监控所有可用的网络(Wi-Fi, Cellular, Ethernet),根据 NetworkCapabilities 和 NetworkScore 决定哪个网络是默认出口(Default Route)。
- Netd 的本质 :内核配置的执行者。它是一个 Native 守护进程,作为 Framework 与 Kernel 之间的桥梁,负责执行具体的网络操作:设置 IP 地址、配置路由表、刷新 DNS、操作 iptables 规则。
- 网络评分的本质 :多维度的权重计算。系统不仅看网络快慢(Bandwidth),还看成本(Metered)、可靠性(Congested)和信任度(Trusted)。
- 策略路由的本质 :多路由表共存 。Android 不使用单一的
main路由表,而是为不同场景(默认、VPN、多用户)创建独立的路由表,并通过ip rule决定数据包走哪张表。
2. 网络架构全景图
2.1 从应用 Socket 到物理网卡
#mermaid-svg-tU14S9Uvo42wJNjf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tU14S9Uvo42wJNjf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tU14S9Uvo42wJNjf .error-icon{fill:#552222;}#mermaid-svg-tU14S9Uvo42wJNjf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tU14S9Uvo42wJNjf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tU14S9Uvo42wJNjf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tU14S9Uvo42wJNjf .marker.cross{stroke:#333333;}#mermaid-svg-tU14S9Uvo42wJNjf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tU14S9Uvo42wJNjf p{margin:0;}#mermaid-svg-tU14S9Uvo42wJNjf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tU14S9Uvo42wJNjf .cluster-label text{fill:#333;}#mermaid-svg-tU14S9Uvo42wJNjf .cluster-label span{color:#333;}#mermaid-svg-tU14S9Uvo42wJNjf .cluster-label span p{background-color:transparent;}#mermaid-svg-tU14S9Uvo42wJNjf .label text,#mermaid-svg-tU14S9Uvo42wJNjf span{fill:#333;color:#333;}#mermaid-svg-tU14S9Uvo42wJNjf .node rect,#mermaid-svg-tU14S9Uvo42wJNjf .node circle,#mermaid-svg-tU14S9Uvo42wJNjf .node ellipse,#mermaid-svg-tU14S9Uvo42wJNjf .node polygon,#mermaid-svg-tU14S9Uvo42wJNjf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tU14S9Uvo42wJNjf .rough-node .label text,#mermaid-svg-tU14S9Uvo42wJNjf .node .label text,#mermaid-svg-tU14S9Uvo42wJNjf .image-shape .label,#mermaid-svg-tU14S9Uvo42wJNjf .icon-shape .label{text-anchor:middle;}#mermaid-svg-tU14S9Uvo42wJNjf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tU14S9Uvo42wJNjf .rough-node .label,#mermaid-svg-tU14S9Uvo42wJNjf .node .label,#mermaid-svg-tU14S9Uvo42wJNjf .image-shape .label,#mermaid-svg-tU14S9Uvo42wJNjf .icon-shape .label{text-align:center;}#mermaid-svg-tU14S9Uvo42wJNjf .node.clickable{cursor:pointer;}#mermaid-svg-tU14S9Uvo42wJNjf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tU14S9Uvo42wJNjf .arrowheadPath{fill:#333333;}#mermaid-svg-tU14S9Uvo42wJNjf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tU14S9Uvo42wJNjf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tU14S9Uvo42wJNjf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tU14S9Uvo42wJNjf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tU14S9Uvo42wJNjf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tU14S9Uvo42wJNjf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tU14S9Uvo42wJNjf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tU14S9Uvo42wJNjf .cluster text{fill:#333;}#mermaid-svg-tU14S9Uvo42wJNjf .cluster span{color:#333;}#mermaid-svg-tU14S9Uvo42wJNjf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tU14S9Uvo42wJNjf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tU14S9Uvo42wJNjf rect.text{fill:none;stroke-width:0;}#mermaid-svg-tU14S9Uvo42wJNjf .icon-shape,#mermaid-svg-tU14S9Uvo42wJNjf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tU14S9Uvo42wJNjf .icon-shape p,#mermaid-svg-tU14S9Uvo42wJNjf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tU14S9Uvo42wJNjf .icon-shape .label rect,#mermaid-svg-tU14S9Uvo42wJNjf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tU14S9Uvo42wJNjf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tU14S9Uvo42wJNjf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tU14S9Uvo42wJNjf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部网络
Linux 内核
Native 层
System Server
应用进程
Socket
决策
Binder IPC
路由劫持
配置
配置
配置
syscall/ioctl
IP Packet
App (HTTP Request)
ConnectivityService
NetworkManagementService
VpnManagerService
Netd (Network Daemon)
dnsmasq (DNS 服务)
Virtual Switch (veth)
Netfilter/iptables
Routing Tables (main/100/200)
Physical NIC (wlan0/rmnet0)
Wi-Fi AP
基站
2.2 核心组件职责表
| 组件 | 层级 | 职责 | 学术定义 |
|---|---|---|---|
| ConnectivityService | Framework | 仲裁者 | 管理 NetworkAgent,计算 NetworkScore,切换默认网络。 |
| NetworkAgent | Framework | 代理 | 每个网络连接的代表(如 Wi-Fi Agent, Cell Agent),向 CS 汇报状态。 |
| Netd | Native | 执行者 | 接收 Framework 命令,通过 syscall 配置内核网络参数。 |
| NetworkManagementService | Framework | 接口 | 封装与 Netd 的 Binder 通信。 |
| Netfilter (iptables) | Kernel | 防火墙 | 过滤数据包,实现 NAT、端口重定向、流量统计。 |
3. ConnectivityService 的评分机制
3.1 网络评分(Network Score)
系统通过 NetworkCapabilities 和 NetworkScore 来评估网络质量。
学术定义:
- Transport Type: 传输类型(Wi-Fi, Cellular, Ethernet, Bluetooth)。
- Capability: 能力(Internet, Not Metered, Trusted, Not Congested)。
- Signal Strength: 信号强度(Wi-Fi RSSI, Cell Signal)。
3.2 评分算法
ConnectivityService 维护一个 NetworkRanker。
| 网络类型 | 基础分 | 扣分因素 | 结果 |
|---|---|---|---|
| Ethernet | 70 | 无 | 70 (最高) |
| Wi-Fi | 60 | 信号弱 (-10), 计费 (-20) | 30 - 60 |
| Cellular (LTE) | 50 | 计费 (-20), 漫游 (-30) | 0 - 30 |
| VPN | 40 | 依赖底层网络 | 继承底层 |
决策逻辑:
- 当 Wi-Fi 连接时,CS 计算 Wi-Fi 得分(例如 55)。
- CS 比较 Wi-Fi (55) 和 Cellular (20)。
- Wi-Fi 胜出,CS 命令 Netd 将默认路由切换到
wlan0。
3.3 网络切换(Handover)
当 Wi-Fi 断开时,CS 会无缝切换到 Cellular。
Kernel Netd ConnectivityService 应用 Kernel Netd ConnectivityService 应用 #mermaid-svg-QVXLUkbzplRfjUvy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QVXLUkbzplRfjUvy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QVXLUkbzplRfjUvy .error-icon{fill:#552222;}#mermaid-svg-QVXLUkbzplRfjUvy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QVXLUkbzplRfjUvy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QVXLUkbzplRfjUvy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QVXLUkbzplRfjUvy .marker.cross{stroke:#333333;}#mermaid-svg-QVXLUkbzplRfjUvy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QVXLUkbzplRfjUvy p{margin:0;}#mermaid-svg-QVXLUkbzplRfjUvy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QVXLUkbzplRfjUvy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-QVXLUkbzplRfjUvy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QVXLUkbzplRfjUvy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-QVXLUkbzplRfjUvy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-QVXLUkbzplRfjUvy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-QVXLUkbzplRfjUvy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-QVXLUkbzplRfjUvy .sequenceNumber{fill:white;}#mermaid-svg-QVXLUkbzplRfjUvy #sequencenumber{fill:#333;}#mermaid-svg-QVXLUkbzplRfjUvy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-QVXLUkbzplRfjUvy .messageText{fill:#333;stroke:none;}#mermaid-svg-QVXLUkbzplRfjUvy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QVXLUkbzplRfjUvy .labelText,#mermaid-svg-QVXLUkbzplRfjUvy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-QVXLUkbzplRfjUvy .loopText,#mermaid-svg-QVXLUkbzplRfjUvy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-QVXLUkbzplRfjUvy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QVXLUkbzplRfjUvy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-QVXLUkbzplRfjUvy .noteText,#mermaid-svg-QVXLUkbzplRfjUvy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-QVXLUkbzplRfjUvy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QVXLUkbzplRfjUvy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QVXLUkbzplRfjUvy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QVXLUkbzplRfjUvy .actorPopupMenu{position:absolute;}#mermaid-svg-QVXLUkbzplRfjUvy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-QVXLUkbzplRfjUvy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QVXLUkbzplRfjUvy .actor-man circle,#mermaid-svg-QVXLUkbzplRfjUvy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-QVXLUkbzplRfjUvy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Wi-Fi 断开 请求网络评估 Wi-Fi (Score: 55)设置默认路由 ->> wlan0网络可用 (Wi-Fi)评估 Cellular (Score: 20)删除 wlan0 路由设置默认路由 ->> rmnet0网络变更 (Cellular)
4. Netd 的执行流水线
4.1 Netd 的模块划分
Netd 是一个非常复杂的守护进程,包含多个模块。
| 模块 | 职责 | 学术定义 |
|---|---|---|
| FirewallController | 防火墙 | 管理 iptables 规则,控制应用联网权限。 |
| RouteController | 路由 | 管理路由表和 ip rule。 |
| DnsController | DNS | 配置 DNS 服务器,管理 dnsmasq。 |
| BandwidthController | 带宽 | 流量统计和限速(Quota)。 |
| NatController | 地址转换 | 配置 SNAT/DNAT,实现热点共享。 |
4.2 命令执行流程
Framework 通过 Binder 向 Netd 发送命令字符串。
示例:设置默认路由
java
// Framework 端
mNetdService.routeAdd("default", "wlan0", "192.168.1.1");
// Netd 端 (CommandListener.cpp)
void CommandListener::RouteCmd::runCommand(...) {
if (strcmp(argv[1], "add") == 0) {
// 调用 libcutils 或执行 iproute2 命令
runIpRouteAdd(argv);
}
}
// 实际执行的 shell 命令
ip route add default via 192.168.1.1 dev wlan0 table 100
5. 策略路由(Policy Routing)
5.1 多路由表机制
Android 不使用默认的 main 路由表,而是为不同场景创建独立的表。
| 路由表 ID | 名称 | 用途 |
|---|---|---|
| 255 | local | 本地路由 |
| 254 | main | 系统默认(通常不用) |
| 253 | default | 备用 |
| 100 | wlan0 | Wi-Fi 路由 |
| 200 | rmnet0 | 蜂窝路由 |
| 61 | oif | 输出接口规则 |
5.2 ip rule 规则
ip rule 决定什么时候 查哪张表。
学术定义:
- Rule: 匹配条件(源 IP、目标 IP、接口、fwmark)。
- Lookup: 要查询的路由表。
Android 的典型规则:
bash
# 查看规则
ip rule show
# 输出:
0: from all lookup local
10000: from all fwmark 0xc0000/0xd0000 lookup legacy_system
13000: from all fwmark 0x10063/0x1ffff lookup local_network
14000: from all oif wlan0 lookup wlan0
15000: from all oif rmnet0 lookup rmnet0
16000: from all lookup main
17000: from all lookup default
解释:
- 如果数据包来自
wlan0接口,查表wlan0。 - 如果数据包带有特定的
fwmark(防火墙标记),查表legacy_system。
6. 防火墙与 iptables
6.1 应用联网控制
当你在设置中禁止某个应用联网时,Netd 实际上是在操作 iptables。
学术定义:
- Owner Match : iptables 的
--uid-owner模块,匹配数据包属于哪个 UID。 - DROP: 丢弃数据包。
命令示例:
bash
# 禁止 UID 10001 (应用) 访问网络
iptables -A OUTPUT -m owner --uid-owner 10001 -j DROP
6.2 VPN 的路由劫持
VPN 的核心是将所有流量重定向到 VPN 隧道接口(tun0)。
学术定义:
- NAT (SNAT): 将源 IP 改为 VPN 服务器的 IP。
- Routing: 添加默认路由指向 tun0。
Netd 操作:
bash
# 创建 tun0 接口
ip link add tun0 type tun
ip addr add 10.0.0.2/24 dev tun0
# 添加默认路由
ip route add default via 10.0.0.1 dev tun0 table 200
# 添加 ip rule
ip rule add from all table 200
7. 关键源码深度解析
7.1 ConnectivityService 的评分逻辑
java
// frameworks/base/services/core/java/com/android/server/connectivity/ConnectivityService.java
private int evaluateNetworkScore(NetworkAgentInfo nai) {
int score = nai.getCurrentScore();
// 如果是计费网络,扣分
if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) == false) {
score -= 20;
}
// 如果信号弱,扣分
int signal = nai.getCurrentSignalStrength();
if (signal < SIGNAL_STRENGTH_THRESHOLD) {
score -= 10;
}
return score;
}
7.2 Netd 的 iptables 调用
cpp
// system/netd/server/FirewallController.cpp
int FirewallController::setUidRule(int childChain, int uid, FirewallRule rule) {
std::string command = "*filter\n";
command += StringPrintf("-A %s -m owner --uid-owner %d -j %s\n",
chainName, uid, rule == ALLOW ? "ACCEPT" : "DROP");
command += "COMMIT\n";
// 通过 socket 发送给 iptables 进程
sendCommand(command);
}
8. 网络管理的常见误区
| 误区 | 学术解释 |
|---|---|
| Wi-Fi 一定比 4G 快 | 不一定。Wi-Fi 信号弱或有线拥塞时,评分可能低于 4G。 |
| VPN 会影响所有流量 | 是的。VPN 通过路由劫持,强制所有流量走 tun0。 |
| 禁止应用联网就是关 Socket | 不是。是通过 iptables 的 Owner Match 在 Kernel 层丢弃数据包。 |
| 多网卡可以叠加网速 | Android 默认不支持。策略路由只能选一条默认路,除非应用自己绑定 Socket 到特定网卡。 |
9. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| ConnectivityService 的本质 | 网络仲裁者,基于 NetworkScore 动态选择默认路由。 |
| Netd 的本质 | 内核配置执行者,通过 iptables 和 iproute2 配置网络。 |
| 策略路由 | 多路由表共存,通过 ip rule 实现基于条件的路由选择。 |
| 防火墙机制 | 基于 UID 的 iptables 过滤,实现应用级联网控制。 |
| VPN 原理 | 路由劫持 + NAT,强制流量进入隧道接口。 |
10. 第八板块结语
至此,第八板块:Android 网络体系与连接管理 已全部完结。
我们从 ConnectivityService 的评分仲裁 出发,深入 Netd 的执行流水线 ,探索 策略路由与 iptables 防火墙 ,最终抵达 VPN 的路由劫持与多网卡管理。
我们揭示了 Android 网络管理的核心逻辑:用评分选择最优路径,用策略路由隔离流量,用防火墙控制访问。
下一篇预告 :第九板块:Android 多媒体体系 | 第二十三篇:AudioFlinger 与 AudioPolicyService 音频架构