Android 15 网络子系统深度解析(二):网络管理策略框架

3 阅读23分钟

Android 15 网络子系统深度解析(二):网络管理策略框架

本文基于 Android 15 源码,深入分析网络管理政策、VPN 框架、性能优化等核心机制

目录

  1. NetworkPolicy 流量控制
  2. VPN 框架深度解析
  3. 网络性能优化
  4. Android 15 新特性

1. NetworkPolicy 流量控制

1.1 NetworkPolicyManagerService 架构

核心设计理念

NetworkPolicyManagerService 是 Android 网络流量管理的核心服务,主要职责包括:

  • 流量限制:按应用、网络接口设置数据流量上限
  • 背景限制:限制后台应用的网络访问
  • 省流模式:数据保护程序实现
  • 按流量计费网络识别:识别不同计费方式的网络
双锁设计模式
mUidRulesFirstLock    - 保护 UID 相关状态(防火墙规则、应用策略)
mNetworkPoliciesSecondLock - 保护网络接口相关状态(网络策略)

锁的分类标记:

  • UL() - 方法需要 mUidRulesFirstLock
  • NL() - 方法需要 mNetworkPoliciesSecondLock
  • AL() - 方法需要所有锁(按顺序获取)

为什么需要双锁?

  • 避免死锁:不同操作涉及不同资源
  • 性能优化:允许并发访问不相关的资源
  • 清晰性:明确标注每个方法的同步需求
核心数据结构
核心状态管理:
├─ 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 - 流量统计

开发建议

  1. 网络策略:尊重用户的省流设置,提供降级方案
  2. VPN 开发:保护好隧道连接,合理设置路由规则
  3. 性能优化:使用连接池、启用压缩、缓存策略
  4. 网络诊断:利用 ConnectivityManager API 进行实时监控