Android 15 网络子系统深度解析(二):网络管理策略框架
本文基于 Android 15 源码,深入分析网络管理政策、VPN 框架、性能优化等核心机制
目录
1. NetworkPolicy 流量控制
1.1 NetworkPolicyManagerService 架构
核心设计理念
NetworkPolicyManagerService 是 Android 网络流量管理的核心服务,主要职责包括:
- 流量限制:按应用、网络接口设置数据流量上限
- 背景限制:限制后台应用的网络访问
- 省流模式:数据保护程序实现
- 按流量计费网络识别:识别不同计费方式的网络
双锁设计模式
mUidRulesFirstLock - 保护 UID 相关状态(防火墙规则、应用策略)
mNetworkPoliciesSecondLock - 保护网络接口相关状态(网络策略)
锁的分类标记:
UL()- 方法需要 mUidRulesFirstLockNL()- 方法需要 mNetworkPoliciesSecondLockAL()- 方法需要所有锁(按顺序获取)
为什么需要双锁?
- 避免死锁:不同操作涉及不同资源
- 性能优化:允许并发访问不相关的资源
- 清晰性:明确标注每个方法的同步需求
核心数据结构
核心状态管理:
├─ mNetworkPolicy // 网络策略映射表
├─ mUidPolicy // UID 的策略(前景、后台等)
├─ mPowerSaveWhitelistAppIds // 电池保护白名单
├─ mMeteredRestrictedUids // 按流量计费网络的受限 UID
└─ mInternetPermissionMap // 应用网络权限映射
监听机制:
├─ mListeners // 网络策略监听器列表
├─ mUidObserver // UID 状态变化观察者
└─ mAlertObserver // 网络告警观察者
初始化流程
1. SystemServer 启动 NetworkStatsService
↓
2. SystemServer 创建 NetworkPolicyManagerService
- 创建 Handler 线程(一般优先级)
- 创建 UidEventThread(高优先级,不允许 I/O)
- 初始化策略文件(netpolicy.xml)
↓
3. bindConnectivityManager()
- 绑定连接管理服务
↓
4. systemReady()
- 加载持久化策略
- 注册 UID 观察者(ActivityManager)
- 监听网络连接变化
- 启动定期检查任务
1.2 Data Saver 省流模式
工作原理
Data Saver(省流模式)通过 iptables 规则实现流量限制:
防火墙链架构:
┌─ bw_INPUT/bw_OUTPUT (全局入口)
│ ├─ bw_global_alert (全局告警链)
│ └─ bw_costly_shared (计费网络链)
│ ├─ bw_penalty_box (eBPF 黑名单)
│ ├─ bw_happy_box (eBPF 白名单)
│ └─ bw_data_saver (省流模式控制)
└─ bw_raw_PREROUTING (原始包处理,用于 ingress 计费)
eBPF 黑名单/白名单机制
Android 15 的关键改进:
在 BandwidthController.cpp 中新增 eBPF 支持:
// eBPF 黑名单规则
"-I bw_penalty_box -m bpf --object-pinned " XT_BPF_DENYLIST_PROG_PATH " -j REJECT"
// eBPF 白名单规则
"-I bw_happy_box -m bpf --object-pinned " XT_BPF_ALLOWLIST_PROG_PATH " -j RETURN"
// eBPF Ingress 计费规则
"-A bw_raw_PREROUTING -m bpf --object-pinned " XT_BPF_INGRESS_PROG_PATH
优势:
- 性能更优:在内核态处理,无需上下文切换
- 灵活性:可动态修改 eBPF 程序而不需要重新加载
- 可观测性:支持 perf 和 BPF 工具链追踪
Data Saver 启用/禁用流程
// NetworkManagementService.setDataSaverModeEnabled()
1. 参数校验
├─ 检查权限
└─ 检查当前状态
2. 如果是 Android V+,使用新的 ConnectivityManager API
└─ ConnectivityManager.setDataSaverEnabled()
3. 否则使用 netd 的旧 API
└─ netdService.bandwidthEnableDataSaver()
4. 更新本地状态
├─ mDataSaverMode = enable
└─ 如果使用计费防火墙链
└─ 更新 FIREWALL_CHAIN_METERED_ALLOW 状态
省流模式对应用的影响
启用省流模式后的应用分类:
1. 允许访问的应用
├─ 前景应用(可见/焦点)
├─ 系统应用
└─ 白名单应用
2. 限制访问的应用
├─ 后台应用
├─ 黑名单应用
└─ 受限应用
1.3 按流量计费网络识别
网络计费标志
NetworkCapabilities 中的关键标志:
1. NET_CAPABILITY_NOT_METERED
- 网络不计费(WiFi、固定网络通常是)
2. NET_CAPABILITY_TEMPORARILY_NOT_METERED
- 临时不计费(如 WiFi 助理)
3. NET_CAPABILITY_NOT_ROAMING
- 非漫游网络
4. 运营商订阅计划
- SubscriptionPlan 指定网络类型和限制
识别机制
来源一:Network Capabilities
// ConnectivityService.java 中的 VPN 合并逻辑
if (!underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
metered |= true; // 基础网络是计费的,VPN 继承
}
来源二:Carrier Config
// NetworkPolicyManagerService 监听运营商配置更新
// 运营商可以通过 CarrierConfig 标记特定网络为计费/非计费
CarrierConfigManager.getConfigForSubId()
来源三:WiFi 设置
// WiFi 可以手动标记为计费或非计费
WifiConfiguration.isMetered()
来源四:用户设置
// 用户可以在 Settings 中手动设置网络计费状态
Settings.Global.MOBILE_METERED // 移动网络
Settings.Global.WIFI_METERED // WiFi
识别流程图
应用查询网络是否计费:
│
├─ 1. 检查 NetworkCapabilities.NOT_METERED
│ └─ 如果存在 → 不计费
│
├─ 2. 检查 SubscriptionPlan
│ ├─ 存在通用计划 → 根据限制判断
│ ├─ 存在特定类型计划 → 根据该计划判断
│ └─ 都不存在 → 继续
│
├─ 3. 检查 Carrier Config
│ └─ 读取运营商配置 → 判断
│
├─ 4. 检查 WiFi/移动设置
│ └─ 读取用户设置 → 判断
│
└─ 5. 默认行为
└─ 移动网络: 计费
WiFi: 不计费
1.4 流量统计与限制
流量统计来源
NetworkStatsService 收集流量数据:
1. /proc/net/xt_qtaguid/stats
- 内核通过 xt_qtaguid 按 UID 统计
- 包括 TCP/UDP 的发送/接收
2. 直接 /proc/net/snmp
- 系统级别的 IP 统计
3. /dev/ipvs_stats (仅特定场景)
- 虚拟服务统计
统计粒度:
├─ 接口级 (interface)
├─ UID 级
├─ 标签级 (tag)
├─ 状态级 (connected/not connected)
└─ 漫游级 (roaming/non-roaming)
限流机制
按接口的全局限制:
// BandwidthController.cpp - 配额规则示例
iptables -N bw_costly_shared
iptables -I bw_INPUT -i rmnet0 -j bw_costly_shared
iptables -I bw_OUTPUT -o rmnet0 -j bw_costly_shared
// 添加配额规则
iptables -A bw_costly_shared -m quota \! --quota 500000 \
-j REJECT --reject-with icmp-net-prohibited
按 UID 的限制:
// 实现方式:eBPF + iptables owner 匹配
iptables -I bw_penalty_box -m owner --uid-owner APP_UID \
-j REJECT --reject-with icmp-port-unreachable
VPN 流量调整:
// NetworkStats.migrateTun() - VPN 流量归属调整
原始统计:
VPN 应用 (uid=10001)
tun0 接口: 1000 MB (包括所有用户流量)
调整后:
VPN 应用: 100 MB (VPN 本身开销)
应用 A: 500 MB (通过 VPN 的流量)
应用 B: 400 MB (通过 VPN 的流量)
2. VPN 框架深度解析
2.1 VpnService 工作原理
VPN 生命周期
1. 准备阶段 (Preparation)
│
├─ VpnService.prepare(context)
│ ├─ 调用 IVpnManager.prepareVpn()
│ ├─ 检查是否已授权
│ └─ 如需授权 → 返回确认 Intent
│
├─ 用户确认后
│ └─ VPN 应用进入 "prepared" 状态
│
└─ 只能有一个 VPN 应用处于 prepared 状态
2. 启动阶段 (Activation)
│
├─ VpnService.startService()
│ └─ onStartCommand() 中建立隧道连接
│
├─ 建立远程服务器连接
│ ├─ TCP/UDP 套接字
│ ├─ SSL/TLS 握手
│ └─ VPN 协议协商(IPSec、WireGuard 等)
│
├─ 调用 Builder.establish()
│ ├─ 创建 TUN 虚拟接口
│ ├─ 配置地址和路由
│ └─ 返回 ParcelFileDescriptor
│
└─ VPN 连接建立完成
3. 数据传输阶段 (Data Transfer)
│
├─ 应用发送数据
│ ├─ 数据包经过 VPN 接口
│ ├─ 被 VPN 应用读取(从 fd)
│ ├─ 加密并发送到服务器
│ └─ 服务器响应
│
├─ 应用接收数据
│ ├─ VPN 应用接收服务器数据
│ ├─ 解密
│ └─ 写入 fd → 系统栈处理 → 应用接收
│
└─ 通过 TUN 接口进行透明转发
4. 撤销阶段 (Revocation)
│
├─ VPN 应用失权或其他应用 prepared
│ └─ IVpnManager.prepareVpn(otherApp)
│
├─ 系统回调 onRevoke()
│ └─ VPN 应用应关闭连接
│
├─ VPN 应用关闭 fd 和隧道
│
└─ 系统移除 VPN 接口和路由规则
核心 API
准备 VPN:
// 需要用户确认
Intent intent = VpnService.prepare(context);
if (intent != null) {
startActivityForResult(intent, REQUEST_VPN_CONFIRM);
}
// 系统应用可以无需用户确认
VpnService.prepareAndAuthorize(context);
建立 VPN:
class MyVpnService extends VpnService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Builder builder = new Builder();
builder.setSession("MyVPN")
.addAddress("192.0.2.2", 24)
.addRoute("0.0.0.0", 0)
.addSearchDomain("example.com");
// 可以添加 DNS
builder.addDnsServer("8.8.8.8");
// 设置底层网络
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
Network[] networks = {cm.getActiveNetwork()};
builder.setUnderlyingNetworks(networks);
// 建立 VPN
fd = builder.establish();
// 启动数据转发线程
startVpnThread();
return START_STICKY;
}
private void startVpnThread() {
new Thread(() -> {
FileInputStream in = new FileInputStream(fd.getFileDescriptor());
FileOutputStream out = new FileOutputStream(fd.getFileDescriptor());
byte[] buffer = new byte[32768];
while (!Thread.interrupted()) {
int len = in.read(buffer);
if (len > 0) {
// 处理数据包
processPacket(buffer, len);
out.write(buffer, 0, len);
}
}
}).start();
}
@Override
public void onRevoke() {
// 清理资源
fd.close();
}
}
保护套接字:
// VPN 应用需要保护自己连接到服务器的套接字
// 否则流量会进入 VPN 引起循环
Socket socket = new Socket();
boolean protected = vpnService.protect(socket.getFileDescriptor());
socket.connect(vpnServerAddress);
2.2 TUN/TAP 虚拟网卡
TUN 和 TAP 的区别
┌─ IP 层处理
│
TUN │ 工作在 IP 层(第 3 层)
│ - 只处理 IP 包(无 MAC 头)
│ - 用于 VPN、路由等 IP 层应用
│ - 性能好、开销小
│
TAP ─────┤
│ 工作在数据链路层(第 2 层)
│ - 处理以太网帧(含 MAC 头)
│ - 用于网桥、虚拟机等
│ - 完整的网络栈模拟
│
TUN 接口创建流程
Java 层 (VpnService.java):
public ParcelFileDescriptor establish() {
// 1. 封装参数
mConfig.addresses = mAddresses; // 配置的 IP 地址
mConfig.routes = mRoutes; // 配置的路由
mConfig.mtu = mMtu; // 最大传输单元
// 2. 调用系统服务
return getService().establishVpn(mConfig);
}
JNI 层 (com_android_server_connectivity_Vpn.cpp):
static int create_interface(int mtu) {
// 1. 打开 TUN 设备
int tun = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
// 2. 分配虚拟接口
ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
ifr4.ifr_flags = IFF_TUN // TUN 模式(不是 TAP)
| IFF_NO_PI; // 不包含包信息头
if (ioctl(tun, TUNSETIFF, &ifr4)) {
// 错误处理
close(tun);
return -1;
}
// 3. 激活接口
ifr4.ifr_flags = IFF_UP;
if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
close(tun);
return -1;
}
// 4. 设置 MTU
ifr4.ifr_mtu = mtu;
if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) {
close(tun);
return -1;
}
return tun;
}
内核中的 TUN 驱动:
/dev/tun 字符设备驱动:
read() 调用:
├─ 从内核数据包队列中取出数据包
├─ 复制到用户缓冲区
└─ 返回包大小
write() 调用:
├─ 从用户缓冲区获取数据包
├─ 设置包元数据
└─ 注入到 IP 栈处理
VPN 应用职责:
├─ read() 循环接收来自网络栈的包
├─ 加密/处理
├─ 通过隧道发送到 VPN 服务器
├─ 接收 VPN 服务器的响应
├─ 解密
└─ write() 注入回网络栈
TUN 接口参数配置
Builder builder = new Builder();
// 1. 设置会话名称(通知栏显示)
builder.setSession("MyVPN");
// 2. 配置 IP 地址
// - 可以配置多个 IPv4/IPv6 地址
builder.addAddress("192.0.2.2", 24); // IPv4
builder.addAddress("fd00::2", 64); // IPv6
// 3. 配置路由
// - 指定哪些流量走 VPN
builder.addRoute("0.0.0.0", 0); // 所有 IPv4 流量
builder.addRoute("::", 0); // 所有 IPv6 流量
// 4. 配置 DNS
builder.addDnsServer("8.8.8.8");
builder.addDnsServer("8.8.4.4");
builder.addSearchDomain("example.com");
// 5. 设置底层网络
// - VPN 本身应该使用哪个网络
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
Network underlying = cm.getActiveNetwork();
builder.setUnderlyingNetworks(new Network[] {underlying});
// 6. 配置应用规则
// - 哪些应用的流量走 VPN
builder.addDisallowedApplication("com.example.bypass"); // 绕过
// 7. 设置 MTU
builder.setMtu(1500);
// 建立连接
ParcelFileDescriptor fd = builder.establish();
2.3 VPN 路由配置
Android VPN 路由模式
模式 1:全局代理(Split Tunneling Off)
应用流量 ──→ VPN 接口 ──→ VPN 应用 ──→ VPN 服务器 ──→ 互联网
(所有流量)
实现方式:
builder.addRoute("0.0.0.0", 0) // 所有 IPv4
builder.addRoute("::", 0) // 所有 IPv6
模式 2:分流代理(Split Tunneling On)
域名应用:
www.example.com ──→ VPN
本地应用:
局域网应用 ──→ 本地网络 ──→ 互联网
实现方式:
builder.addRoute("10.8.0.0", 24) // 只代理特定网段
builder.excludeRoute("192.168.0.0", 16) // 排除本地网络
底层网络的重要性
builder.setUnderlyingNetworks(Network[] networks)
作用:
1. 告诉系统 VPN 应该使用哪个网络
2. 系统会根据底层网络能力设置 VPN 的 capabilities
3. 影响应用如何选择网络
示例:
Network[] networks = {wifiNetwork, mobileNetwork};
builder.setUnderlyingNetworks(networks);
结果:
- VPN 会继承这些网络的计费、漫游等属性
- VPN 的流量会统计到这些网络上
路由优先级
Android 网络栈中的路由查询顺序:
1. 直连路由(已连接接口)
├─ 优先级最高
└─ 如:192.168.1.0/24 → eth0
2. VPN 接口路由
├─ 如果配置了 addRoute()
└─ 如:0.0.0.0/0 → tun0
3. 默认路由
├─ 通常指向 WiFi 或移动网络
└─ 如:0.0.0.0/0 → wlan0
典型场景:
应用访问 8.8.8.8
├─ 查找直连路由 → 没有
├─ 查找 VPN 路由 → 0.0.0.0/0 → tun0 (匹配)
├─ 路由到 VPN 接口
└─ VPN 应用处理
2.4 Always-on VPN
实现原理
Always-on VPN 的特殊之处:
普通 VPN:
应用主动启动 VPN 服务
└─ 应用崩溃 → VPN 断开
Always-on VPN:
系统自动启动 VPN 服务
- 开机后自动连接
- VPN 应用崩溃后自动重启
- 用户无法禁用(需要进入设置关闭)
配置位置:
Settings → Network & internet → VPN → [VPN 名称] → Always-on VPN
内部实现:
1. Settings 存储配置
Settings.Secure.VPN_APP = "com.example.vpn"
Settings.Secure.VPN_REQUIRE_CONNECTION = true
2. ConnectivityService 启动时检查
├─ 读取设置
├─ 如果启用 Always-on VPN
└─ 启动对应 VPN 应用
3. ActivityManager 监控 VPN 进程
├─ 如果进程死亡
├─ 重启 VPN 应用
└─ 恢复 VPN 连接
Block Connections Without VPN
当 Always-on VPN 断开时的行为:
// 如果启用 "Block connections without VPN"
Settings.Secure.BLOCK_UNENCRYPTED_TRAFFIC = true
结果:
1. VPN 断开或未连接
2. 系统断开所有非 VPN 网络连接
3. 应用无法使用其他网络
实现方式:
- 防火墙规则阻止所有非 VPN 流量
- 只允许 VPN 应用和系统应用通过
- VPN 一旦连接立即解除限制
Always-on VPN 的安全性
Always-on VPN 保护的隐私:
流量保护:
├─ 所有流量必须通过 VPN
├─ DNS 查询通过 VPN
└─ 即使 VPN 断开也无法泄露
应用层隐私:
├─ VPN 配置对用户透明
├─ 用户无法轻易关闭
└─ 适合企业/管理场景
潜在风险:
├─ VPN 提供商可以监视所有流量
├─ VPN 应用需要充分信任
└─ 权限提升后可能被滥用
3. 网络性能优化
3.1 流量优化技巧
DNS 查询优化
多级 DNS 缓存:
应用 DNS 查询:
│
├─ 1. 应用本地缓存 (如果有)
│ └─ 命中率: 80-90%
│
├─ 2. System DNS Resolver (libnetd_resolv)
│ ├─ 缓存 TTL 最小值: 1 秒
│ ├─ 缓存 TTL 最大值: 45 秒
│ └─ 命中率: 30-50%
│
├─ 3. ISP DNS 或自定义 DNS
│ ├─ 查询延迟: 50-200ms
│ └─ 可能被 DNS 污染
│
└─ 4. Authoritative Name Server
└─ 递归查询: 200-500ms
DNS 查询参数优化:
BIND 9 兼容实现中的优化:
1. 并行查询
├─ 同时向多个 DNS 服务器查询
├─ 选择最快的响应
└─ 时间收益: 30-40%
2. DNS over HTTPS/TLS (DoH/DoT)
├─ 隐私保护: 防止中间人监听
├─ 延迟增加: 10-20ms
├─ Android 支持: Settings 中配置
└─ 建议场景: 公网场景
3. DNSSEC 验证
├─ 完整性验证
├─ CPU 开销: 5-10%
├─ 适合企业
└─ 不适合移动设备
4. DNS 预测和预加载
├─ 根据用户行为预测常访问域名
├─ 提前查询和缓存
├─ 延迟降低: 20-50%
└─ 需要应用支持
连接池管理
HTTP 连接复用(Keep-Alive):
场景对比:
不使用 Keep-Alive:
请求 1: TCP 握手 (100ms) + HTTP 请求 (50ms) = 150ms
请求 2: TCP 握手 (100ms) + HTTP 请求 (50ms) = 150ms
请求 3: TCP 握手 (100ms) + HTTP 请求 (50ms) = 150ms
总耗时: 450ms
使用 Keep-Alive:
请求 1: TCP 握手 (100ms) + HTTP 请求 (50ms) = 150ms
请求 2: HTTP 请求 (50ms) = 50ms
请求 3: HTTP 请求 (50ms) = 50ms
总耗时: 250ms (降低 44%)
OkHttp 连接池配置:
OkHttpClient client = new OkHttpClient.Builder()
// 最大连接数
.connectionPool(new ConnectionPool(
32, // 最大空闲连接数
5, // 连接保活时间
TimeUnit.MINUTES // 时间单位
))
// 连接超时
.connectTimeout(10, TimeUnit.SECONDS)
// 读超时
.readTimeout(10, TimeUnit.SECONDS)
// 写超时
.writeTimeout(10, TimeUnit.SECONDS)
// 重试和重定向
.retryOnConnectionFailure(true)
.followRedirects(true)
.followSslRedirects(true)
// 连接拦截器(可以添加日志、重试等逻辑)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 添加 Keep-Alive 头
request = request.newBuilder()
.header("Connection", "Keep-Alive")
.build();
return chain.proceed(request);
}
})
.build();
流量压缩
Gzip 压缩:
响应体大小对比:
JSON 数据: 100 KB
├─ 未压缩: 100 KB
├─ Gzip 压缩: 10-15 KB (压缩率 85-90%)
└─ 传输时间: 2 秒 → 0.2 秒 (快 10 倍)
图片数据: 200 KB
├─ 未压缩: 200 KB
├─ PNG/JPEG: 200 KB (已压缩格式)
└─ Gzip 套压: 200-210 KB (效果不佳)
建议:
- 文本/JSON: 启用 Gzip
- 图片/视频: 不需要 Gzip (已是压缩格式)
OkHttp 自动 Gzip:
// OkHttp 会自动添加以下头
GET /api/data HTTP/1.1
Accept-Encoding: gzip, deflate
Content-Encoding: gzip
// 服务器响应
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 15000
// OkHttp 自动解压,应用透明处理
缓存策略
HTTP 缓存头的使用:
Cache-Control 指令:
1. max-age=3600
├─ 缓存有效期 3600 秒(1 小时)
├─ 无需与服务器通信
└─ 减少流量和延迟
2. must-revalidate
├─ 必须验证新鲜度
├─ 过期后需要服务器确认
└─ 保证数据最新性
3. public/private
├─ public: 任何缓存都可以缓存
├─ private: 只有浏览器/客户端缓存
└─ 影响代理缓存
4. no-cache/no-store
├─ no-cache: 缓存可以使用,但需要验证
├─ no-store: 不允许任何缓存
└─ 用于敏感数据
ETag 和 Last-Modified 的作用:
ETag 验证:
客户端缓存了数据(ETag: "abc123")
下次请求:
GET /api/data
If-None-Match: "abc123"
服务器响应:
1. 数据未变
├─ HTTP 304 Not Modified
├─ 无响应体
└─ 节省带宽: 减少 95%
2. 数据已变
├─ HTTP 200 OK
├─ 返回新数据
└─ 更新缓存
3.2 DNS 优化
DoH (DNS over HTTPS)
优点:
1. 隐私保护
├─ DNS 查询加密
├─ 运营商无法知道访问哪些网站
└─ 防止 DNS 污染
2. 安全性
├─ 防止 MITM 攻击
├─ 防止 DNS 伪装
└─ 验证 DNS 服务器真实性
3. 性能(在特定场景)
├─ 可以利用 HTTP/2 的多路复用
├─ 减少连接建立开销
└─ 在网络较好时更快
缺点:
1. 延迟增加
├─ TCP 握手: ~50ms
├─ TLS 握手: ~50ms
├─ HTTP 请求: ~50ms
└─ 总计: 100-150ms (vs 传统 DNS 的 20-50ms)
2. 兼容性
├─ 老版本 Android 不支持
├─ 某些 DNS 服务器不支持
└─ 可能被企业网络屏蔽
3. CPU 开销
├─ TLS 加密/解密
├─ HTTP 序列化
└─ 增加 CPU 使用 5-10%
Android 中的 DoH 配置:
Settings → Network & internet → Private DNS
支持的提供商:
1. Google - dns.google
2. Cloudflare - 1.1.1.1
3. Quad9 - quad9.net
实现原理:
- 通过 ConnectivityManager 设置
- 系统 DNS 解析器转发查询到 DoH 端点
- 对应用完全透明
智能 DNS 选择
分域名 DNS:
根据域名类型选择不同 DNS:
1. 国内域名
└─ 使用运营商 DNS (ISP DNS)
├─ 缓存好
├─ 响应快
├─ 缺点: 可能被污染
2. 海外域名
└─ 使用公共 DNS (Google/Cloudflare)
├─ 无污染
├─ 但可能较慢
└─ 因为需要跨越长距离
3. 中转域名 (CDN)
└─ 选择距离最近的 DNS 服务器
├─ 减少查询延迟
└─ 获得最优 CDN 节点
实现示例:
public class SmartDnsResolver {
private String chinaRegex = ".*\\.(cn|中国|公司|政务)$";
private String overseasRegex = ".*\\.(com|org|net|io)$";
public String selectDns(String domain) {
if (domain.matches(chinaRegex)) {
// 国内域名 - 使用 ISP DNS
return "192.168.1.1"; // 运营商 DNS
} else if (domain.matches(overseasRegex)) {
// 海外域名 - 使用公共 DNS
return "8.8.8.8"; // Google DNS
} else {
// 默认
return SystemDnsResolver.getDefault();
}
}
}
3.3 连接池管理
TCP 连接池的最优大小
连接池大小与性能的关系:
连接池大小 性能 缺点
1-5 低(频繁 GC) 大量 TCP 握手
10-20 中等 开销小
32-64 高 标准配置
100+ 略高 内存使用增加
200+ 低下 内存不足,GC 压力大
建议配置:
╔════════════════════════════════╗
║ API 端点类型 最大连接数 ║
╠════════════════════════════════╣
║ 单个 API 服务 8-16 ║
║ 多个 API 服务 32-64 ║
║ CDN 下载服务 64-128 ║
║ 消息推送服务 128-256 ║
╚════════════════════════════════╝
TCP 保活机制
TCP Keep-Alive:
问题:长连接中间断开无感知
TCP Keep-Alive 工作流程:
├─ 设置 TCP_KEEP_ALIVE
├─ 连接空闲 2 小时后
├─ 发送保活探针
├─ 如果无响应
│ └─ 每 75 秒重试(共 9 次)
└─ 9 次都无响应则断开连接
代码配置:
Socket socket = new Socket();
socket.setKeepAlive(true);
socket.setSoTimeout(30000); // 30 秒读超时
HTTP Keep-Alive vs TCP Keep-Alive:
HTTP Keep-Alive:
├─ 应用层
├─ HTTP/1.1 默认启用
├─ Connection: Keep-Alive
└─ 用于复用 TCP 连接传输多个 HTTP 请求
TCP Keep-Alive:
├─ 传输层
├─ 检测僵尸连接
├─ SO_KEEPALIVE socket 选项
└─ 用于长连接的连通性检测
两者配合使用最优。
3.4 网络诊断工具
ConnectivityManager 诊断 API
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
// 1. 获取当前网络
Network network = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(network);
// 2. 检查网络能力
boolean hasInternet = caps.hasCapability(NET_CAPABILITY_INTERNET);
boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
boolean isVPN = caps.hasCapability(NET_CAPABILITY_NOT_VPN);
// 3. 获取链接信息
LinkProperties linkProps = cm.getLinkProperties(network);
List<InetAddress> dnsServers = linkProps.getDnsServers();
// 4. 获取带宽信息
int downBW = caps.getLinkDownstreamBandwidthKbps();
int upBW = caps.getLinkUpstreamBandwidthKbps();
// 5. 注册网络变化监听
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.build();
cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
// 网络可用
}
@Override
public void onLost(Network network) {
// 网络断开
}
@Override
public void onBlockedStatusChanged(Network network, boolean blocked) {
// 网络被阻止(如后台限制)
}
});
NetworkStatsManager 流量统计
NetworkStatsManager nsm = getSystemService(NetworkStatsManager.class);
// 1. 获取移动网络统计
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
try {
bucket = nsm.querySummaryForDevice(
ConnectivityManager.TYPE_MOBILE,
getSubscriberId(),
System.currentTimeMillis() - 24 * 3600 * 1000,
System.currentTimeMillis()
);
long rxBytes = bucket.getRxBytes(); // 接收字节
long txBytes = bucket.getTxBytes(); // 发送字节
long rxPackets = bucket.getRxPackets();
long txPackets = bucket.getTxPackets();
} catch (RemoteException e) {
// 处理错误
}
// 2. 获取应用级统计
NetworkStats stats = nsm.queryDetailsForUid(
ConnectivityManager.TYPE_MOBILE,
getSubscriberId(),
System.currentTimeMillis() - 7 * 24 * 3600 * 1000,
System.currentTimeMillis(),
myUid
);
while (stats.hasNextBucket()) {
bucket = new NetworkStats.Bucket();
stats.getNextBucket(bucket);
// 按接口分类的流量
String iface = bucket.getState();
long bytes = bucket.getRxBytes() + bucket.getTxBytes();
}
Ping 和网络连通性检测
public class NetworkDiagnostics {
public static boolean isHostReachable(String host, int timeout) {
try {
InetAddress.getByName(host).isReachable(timeout);
return true;
} catch (IOException e) {
return false;
}
}
public static long ping(String host) throws IOException {
long start = System.nanoTime();
Runtime.getRuntime().exec(new String[] {"/system/bin/ping", "-c", "1", host})
.waitFor();
return (System.nanoTime() - start) / 1_000_000; // 毫秒
}
public static void diagnoseNetwork() {
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
Network network = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(network);
System.out.println("=== Network Diagnosis ===");
System.out.println("Internet: " +
caps.hasCapability(NET_CAPABILITY_INTERNET));
System.out.println("Metered: " +
!caps.hasCapability(NET_CAPABILITY_NOT_METERED));
System.out.println("VPN: " +
caps.hasCapability(NET_CAPABILITY_NOT_VPN));
System.out.println("Down BW: " +
caps.getLinkDownstreamBandwidthKbps() + " kbps");
System.out.println("Up BW: " +
caps.getLinkUpstreamBandwidthKbps() + " kbps");
// DNS 检测
LinkProperties linkProps = cm.getLinkProperties(network);
System.out.println("DNS: " +
linkProps.getDnsServers());
}
}
4. Android 15 新特性
4.1 eBPF 网络策略
eBPF 简介
eBPF (Extended Berkeley Packet Filter):
传统方式(iptables):
用户空间: iptables 命令
↓
系统调用: netlink 通信
↓
内核空间: 创建 iptables 规则
↓
包处理时: 遍历规则链
问题:
├─ 灵活性差: 修改规则需要重新加载
├─ 性能开销: 每次都要遍历所有规则
└─ 复杂性: 规则链深时性能下降
eBPF 方式:
用户空间: 编写 eBPF 程序
↓
编译: LLVM 编译成 eBPF 字节码
↓
加载: 通过 bpf() 系统调用加载
↓
内核态执行: JIT 编译成本地代码
↓
性能: 接近本地代码速度
Android 15 中的 eBPF 应用
防火墙规则加速:
// BandwidthController.cpp 中的 eBPF 集成
// 黑名单规则(使用 eBPF)
"-I bw_penalty_box -m bpf --object-pinned "
XT_BPF_DENYLIST_PROG_PATH " -j REJECT"
// 白名单规则(使用 eBPF)
"-I bw_happy_box -m bpf --object-pinned "
XT_BPF_ALLOWLIST_PROG_PATH " -j RETURN"
// Ingress 计费规则(使用 eBPF)
"-A bw_raw_PREROUTING -m bpf --object-pinned "
XT_BPF_INGRESS_PROG_PATH
优势:
性能对比:
iptables 规则链:
黑名单规则检查: 100ns × 规则数
100 条规则: 10μs
eBPF 程序:
HashMap 查找: 100ns
无论规则数多少: ~100-200ns
性能提升: 50-100 倍(规则多时)
eBPF 程序结构示例:
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
BPF_ARRAY(denylist, __u32, 10000);
SEC("xt_bpf")
int filter_denylist(struct __sk_buff *skb) {
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
// 解析以太网头
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return 1; // 允许传递
if (eth->h_proto != htons(ETH_P_IP))
return 1;
// 解析 IP 头
struct iphdr *ip = (struct iphdr *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return 1;
// 获取发送方 UID (内核会填充)
__u32 uid = skb->uid;
// 在黑名单中查找
if (denylist.lookup(&uid)) {
return 0; // 拒绝
}
return 1; // 允许
}
eBPF 的可观测性
使用 BPF 工具链进行诊断:
1. bpftool 检查加载的 eBPF 程序
$ bpftool prog list
$ bpftool prog show
2. bpftrace 动态追踪
$ bpftrace -e 'kprobe:tcp_connect { print("connection") }'
3. perf 记录 eBPF 事件
$ perf record -e bpf_prog_run ...
$ perf report
优点:
├─ 低开销: 仅当事件发生时记录
├─ 详细: 可以记录所有包的元数据
└─ 灵活: 不需要重启即可修改追踪规则
4.2 VPN 改进
VPN 的新增能力
多底层网络支持:
Android 14 及之前:
VPN 只能绑定一个底层网络
Android 15:
VPN 可以绑定多个底层网络
实现:
builder.setUnderlyingNetworks(new Network[] {
wifiNetwork,
mobileNetwork,
ethernetNetwork
});
优点:
├─ 网络切换时自动转换(无缝)
├─ 多网络链接时负载均衡
├─ 更好的网络冗余
└─ 企业 VPN 配置更灵活
改进的路由管理:
// Android 15 新增 API
builder.addExclusiveRoute(RouteInfo route)
说明:
├─ 独占路由: 只有 VPN 应用可以使用
├─ 用于 VPN 隧道本身的流量
└─ 防止流量循环
示例:
RouteInfo vpnTunnelRoute = new RouteInfo(
new IpPrefix("10.8.0.0", 24),
null, // 网关
"tun0"
);
builder.addExclusiveRoute(vpnTunnelRoute);
Allow/Disallow 应用的改进:
Android 15 之前:
├─ addAllowedApplication() - 只有这些应用走 VPN
├─ addDisallowedApplication() - 这些应用不走 VPN
└─ 两个不能同时使用
Android 15:
├─ 支持混合模式
├─ 可以更灵活地指定应用
└─ 支持按权限分类
企业 VPN 增强
Always-on VPN 的改进:
1. 管理员控制
├─ 通过 DevicePolicyManager 设置
└─ 用户无法禁用
2. 应用隔离
├─ 工作应用通过企业 VPN
├─ 个人应用绕过 VPN
└─ 基于工作资料控制
3. 通知管理
├─ 企业 VPN 状态通知
├─ 支持自定义通知文本
└─ 支持自定义图标
4. 密钥管理
├─ 通过系统密钥库存储 VPN 凭证
├─ 支持生物识别解锁
└─ 防止凭证被其他应用访问
4.3 网络切片支持
网络切片(Network Slicing)简介
网络切片 (5G SA 特性):
传统网络:
┌─────────────────────────────────────┐
│ 运营商网络 │
│ ┌──────────────────────────────┐ │
│ │ 网络资源 (频谱、基站等) │ │
│ └──────────────────────────────┘ │
│ (按最大容量规划) │
└─────────────────────────────────────┘
网络切片:
┌─────────────────────────────────────┐
│ 运营商网络 (共享) │
│ ┌──────────┐ ┌──────────┐ ┌─────┐ │
│ │ 切片 1 │ │ 切片 2 │ │ 切片3│ │
│ │(高带宽) │ │(低延迟) │ │(物联)│ │
│ │ (视频) │ │ (游戏) │ │(M2M)│ │
│ └──────────┘ └──────────┘ └─────┘ │
│ (共享基础设施,按需分配) │
└─────────────────────────────────────┘
优点:
├─ 资源利用率: 提高 40-60%
├─ 成本: 降低运营成本 30-40%
├─ 服务质量: 针对性的 SLA
└─ 创新: 支持新业务模式
Android 中的网络切片 API
查询网络切片信息:
TelephonyManager tm = getSystemService(TelephonyManager.class);
// 获取当前数据网络的切片标识 (NSSAI)
NetworkSliceInfo sliceInfo = tm.getNetworkSliceInfo();
if (sliceInfo != null) {
// SST (Slice/Service Type)
int sst = sliceInfo.getSliceServiceType();
// NSSAI (Network Slice Selection Assistance Information)
String nssai = sliceInfo.getSliceInfo();
Log.d(TAG, "Slice SST: " + sst + ", NSSAI: " + nssai);
}
// SST 类型定义
// 1 - 增强移动宽带 (eMBB)
// 2 - 超可靠低延迟 (URLLC)
// 3 - 大规模机器通信 (mMTC)
请求特定切片:
TelephonyManager tm = getSystemService(TelephonyManager.class);
// 请求 URLLC 切片 (低延迟)
List<NetworkSliceInfo> desiredSlices = Arrays.asList(
new NetworkSliceInfo(
NetworkSliceInfo.SLICE_TYPE_URLLC, // 低延迟
null // 可选的 S-NSSAI
)
);
// 某些应用(如游戏、直播)可以请求特定切片
tm.requestNetworkSlice(desiredSlices, executor, callback);
网络切片的应用场景
场景 1: 高清直播
需求: 高带宽、低延迟
使用: eMBB 切片
┌─────────────────┐
│ 高清直播应用 │
├─────────────────┤
│ WiFi / eMBB切片 │
├─────────────────┤
│ 带宽: 50+ Mbps │
│ 延迟: 100ms │
└─────────────────┘
场景 2: 实时游戏
需求: 超低延迟、可靠性
使用: URLLC 切片
┌─────────────────┐
│ 实时游戏 │
├─────────────────┤
│ URLLC 切片 │
├─────────────────┤
│ 带宽: 10 Mbps │
│ 延迟: < 50ms │
└─────────────────┘
场景 3: 物联网设备
需求: 低功耗、大连接
使用: mMTC 切片
┌─────────────────┐
│ IoT 传感器 │
├─────────────────┤
│ mMTC 切片 │
├─────────────────┤
│ 带宽: 1 Mbps │
│ 功耗: 极低 │
└─────────────────┘
切片选择的最佳实践
public class NetworkSliceSelector {
// 根据应用类型选择合适的切片
public NetworkSliceInfo selectSlice(AppType appType) {
switch (appType) {
case VIDEO_STREAMING:
// 视频流需要高带宽
return new NetworkSliceInfo(
NetworkSliceInfo.SLICE_TYPE_EMBB,
"01" // S-NSSAI
);
case ONLINE_GAMING:
// 游戏需要低延迟
return new NetworkSliceInfo(
NetworkSliceInfo.SLICE_TYPE_URLLC,
"02"
);
case IOT_DEVICE:
// 物联网需要低功耗
return new NetworkSliceInfo(
NetworkSliceInfo.SLICE_TYPE_MMTC,
"03"
);
default:
return null; // 使用默认切片
}
}
// 性能监控
public void monitorSlicePerformance(Network network) {
TelephonyManager tm = getSystemService(TelephonyManager.class);
// 获取当前切片
NetworkSliceInfo currentSlice = tm.getNetworkSliceInfo();
// 监测网络质量
// 如果性能不满足需求,可以请求重新选择
}
}
总结
Android 15 网络子系统的改进体现在以下几个方面:
架构设计
- 双锁模式:清晰的同步设计,避免死锁
- 分层管理:从应用层到内核层的完整体系
- 事件驱动:高效的异步通知机制
性能优化
- eBPF 加速:从内核级别优化防火墙性能
- 多网络支持:VPN 可同时使用多个底层网络
- 智能 DNS:支持 DoH/DoT 和分域名解析
安全增强
- 细粒度控制:按应用、按网络的流量限制
- 隐私保护:Always-on VPN 和分流代理
- 企业管理:DevicePolicyManager 集成
新技术应用
- 网络切片:5G 差异化服务支持
- eBPF 可观测性:更好的诊断和追踪
- 防火墙链优化:计费网络的专用规则
这些改进共同为开发者提供了更强大、更灵活、更高效的网络管理工具。
参考资源
核心文件路径
frameworks_base/services/core/java/com/android/server/net/NetworkPolicyManagerService.java- 网络策略管理frameworks_base/core/java/android/net/VpnService.java- VPN 服务基类frameworks_base/services/core/java/com/android/server/connectivity/Vpn.java- VPN 实现system_netd/server/BandwidthController.cpp- 带宽控制(eBPF)
相关 API
android.net.NetworkPolicyManager- 网络策略android.net.VpnService- VPN 开发android.net.ConnectivityManager- 连接管理android.app.usage.NetworkStatsManager- 流量统计
开发建议
- 网络策略:尊重用户的省流设置,提供降级方案
- VPN 开发:保护好隧道连接,合理设置路由规则
- 性能优化:使用连接池、启用压缩、缓存策略
- 网络诊断:利用 ConnectivityManager API 进行实时监控