引言
在上一篇文章中,我们深入探索了ConnectivityService的网络管理框架。今天,我们将聚焦于网络子系统的另外两个核心功能:网络策略管理和VPN框架。
为什么需要网络策略管理?
想象这样的场景:
场景1:流量失控
用户月底收到运营商短信:"您的流量已用完95%"
查看系统设置发现:
- 某个后台应用偷偷下载了2GB更新包
- 多个应用在后台同步数据
- 系统服务持续上传统计数据
场景2:电量告急
用户外出发现:
- 手机只剩15%电量
- 但至少还要2小时才能充电
- 很多应用还在后台联网消耗电量
这时候,Android的**NetworkPolicy(网络策略)**系统就派上用场了!
本文内容概览
-
NetworkPolicy流量控制
- NetworkPolicyManagerService架构
- Data Saver省流模式
- 按流量计费网络识别
- 流量统计与限制
-
VPN框架深度解析
- VpnService工作原理
- TUN/TAP虚拟网卡
- VPN路由配置
- Always-on VPN
-
网络性能优化
- 流量优化技巧
- DNS优化
- 连接池管理
- 网络诊断工具
-
Android 15新特性
- eBPF网络策略
- VPN改进
- 网络切片支持
让我们开始这段深入Android网络核心的旅程!
一、NetworkPolicy流量控制深度解析
1.1 NetworkPolicyManagerService架构总览
源码位置:
packages/modules/Connectivity/service/src/com/android/server/net/NetworkPolicyManagerService.java
架构图
核心职责:
┌────────────────────────────────────────┐
│ NetworkPolicyManagerService 核心功能 │
├────────────────────────────────────────┤
│ 1. 流量监控 │
│ - 实时统计应用流量 │
│ - 按UID/Interface统计 │
│ - 历史流量记录 │
│ │
│ 2. 策略执行 │
│ - Data Saver模式 │
│ - App Standby限制 │
│ - 按流量计费网络控制 │
│ │
│ 3. 流量限制 │
│ - 后台数据禁用 │
│ - 流量配额管理 │
│ - 防火墙规则 │
│ │
│ 4. 网络识别 │
│ - Metered/Unmetered区分 │
│ - WiFi/移动网络判断 │
│ - 运营商计费策略 │
└────────────────────────────────────────┘
核心数据结构
// packages/modules/Connectivity/.../NetworkPolicyManagerService.java
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// 网络策略集合 (按NetworkTemplate分类)
private final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy =
new ArrayMap<>();
// UID策略 (每个应用的网络限制策略)
private final SparseIntArray mUidPolicy = new SparseIntArray();
// UID规则 (防火墙规则:允许/拒绝)
private final SparseIntArray mUidRules = new SparseIntArray();
// 按流量计费网络集合
private final ArraySet<NetworkTemplate> mMeteredIfaces = new ArraySet<>();
// Data Saver模式开关
private boolean mRestrictBackground = false;
// 核心方法1:设置UID网络策略
@Override
public void setUidPolicy(int uid, int policy) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
// 1. 更新策略
if (policy == POLICY_NONE) {
mUidPolicy.delete(uid);
} else {
mUidPolicy.put(uid, policy);
}
// 2. 更新防火墙规则
updateRulesForDataUsageRestrictionsUL(uid);
// 3. 持久化到磁盘
writePolicyAL();
// 4. 通知监听器
mHandler.obtainMessage(MSG_POLICIES_CHANGED, uid, policy).sendToTarget();
}
// 核心方法2:更新防火墙规则
private void updateRulesForDataUsageRestrictionsUL(int uid) {
// 计算最终的网络规则
final int uidRules = mUidRules.get(uid, RULE_NONE);
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
// 规则改变才更新
if (uidRules == oldUidRules) return;
// 更新规则到Kernel
setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uid,
uidRules & RULE_REJECT_METERED);
setUidFirewallRulesUL(FIREWALL_CHAIN_DOZABLE, uid,
uidRules & RULE_REJECT_ALL);
setUidFirewallRulesUL(FIREWALL_CHAIN_POWERSAVE, uid,
uidRules & RULE_REJECT_ALL);
// Android 15: 使用eBPF代替iptables
if (mUseBpfTrafficStats) {
mBpfNetMaps.setUidRule(uid, uidRules);
}
}
// 核心方法3:设置Data Saver模式
@Override
public void setRestrictBackground(boolean restrictBackground) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
if (mRestrictBackground == restrictBackground) return;
mRestrictBackground = restrictBackground;
// 更新所有应用的网络规则
updateRulesForRestrictBackgroundUL();
// 通知系统服务
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED,
restrictBackground ? 1 : 0, 0).sendToTarget();
}
}
1.2 Data Saver省流模式详解
Data Saver是Android 7.0引入的省流功能,在Android 15中得到了eBPF的性能增强。
工作原理
用户开启Data Saver后:
1. 后台应用网络被禁用
→ 除非应用在白名单中
2. 前台应用可以正常联网
→ 但会收到DATA_SAVER_ENABLED回调
3. 应用可以请求白名单权限
→ POLICY_ALLOW_METERED_BACKGROUND
4. 系统服务不受影响
→ UID < 10000的系统进程豁免
应用示例
// 应用层 - 检测Data Saver状态
public class DataSaverChecker {
private ConnectivityManager mCm;
public void checkDataSaver(Context context) {
mCm = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
// 1. 获取Data Saver状态
int status = mCm.getRestrictBackgroundStatus();
switch (status) {
case RESTRICT_BACKGROUND_STATUS_DISABLED:
// Data Saver关闭,无限制
Log.d(TAG, "Data Saver关闭");
break;
case RESTRICT_BACKGROUND_STATUS_ENABLED:
// Data Saver开启,后台数据被禁用
Log.w(TAG, "Data Saver开启,后台数据受限");
// 建议:延迟非紧急的网络请求
postponeNonUrgentRequests();
break;
case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
// 应用在白名单中,不受限制
Log.d(TAG, "应用在Data Saver白名单");
break;
}
}
// 2. 监听Data Saver变化
public void registerDataSaverListener(Context context) {
mCm.registerDefaultNetworkCallback(new NetworkCallback() {
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities nc) {
// 检查是否有NOT_METERED能力
boolean isMetered = !nc.hasCapability(
NET_CAPABILITY_NOT_METERED);
if (isMetered && isDataSaverEnabled()) {
// 在按流量计费网络 + Data Saver开启
showDataSaverWarning();
}
}
});
}
// 3. 应用优化建议
private void optimizeForDataSaver() {
if (isDataSaverEnabled()) {
// 减少预加载
disablePrefetching();
// 降低图片质量
setImageQuality(QUALITY_LOW);
// 禁用自动播放视频
disableAutoplayVideos();
// 延迟大文件下载
pauseLargeDownloads();
}
}
}
Framework层实现
// NetworkPolicyManagerService.java
private void updateRulesForRestrictBackgroundUL() {
// 遍历所有UID
final int size = mUidPolicy.size();
for (int i = 0; i < size; i++) {
final int uid = mUidPolicy.keyAt(i);
final int policy = mUidPolicy.valueAt(i);
// 计算规则
int uidRules = RULE_NONE;
if (mRestrictBackground) {
// Data Saver开启
if (!hasWhitelistExemption(policy)) {
// 不在白名单,禁止后台数据
uidRules |= RULE_REJECT_METERED;
}
}
// 应用规则
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, uidRules);
}
}
// 检查白名单
private boolean hasWhitelistExemption(int policy) {
// 允许按流量计费网络的后台数据
return (policy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
}
1.3 流量统计NetworkStatsService
核心功能:实时统计应用和网络接口的流量数据。
// packages/modules/Connectivity/.../NetworkStatsService.java
public class NetworkStatsService extends INetworkStatsService.Stub {
// UID维度统计
private final NetworkStatsRecorder mUidRecorder;
// Interface维度统计
private final NetworkStatsRecorder mUidTagRecorder;
// 核心方法:获取UID流量统计
@Override
public NetworkStats getUidStats(int uid, NetworkTemplate template) {
// 1. 从BPF读取统计数据 (Android 15)
NetworkStats stats = mBpfNetMaps.readStatsForUid(uid);
// 2. 合并历史数据
stats.combineWith(mUidRecorder.getOrLoadHistoryFor(uid, template));
// 3. 返回结果
return stats;
}
// 轮询更新统计数据
private void performPoll(int flags) {
// 1. 从Kernel读取最新数据
final NetworkStats xtSnapshot = readNetworkStatsUidDetail(UID_ALL);
// 2. 计算增量
final NetworkStats delta = xtSnapshot.subtract(mLastStatsSnapshot);
mLastStatsSnapshot = xtSnapshot;
// 3. 记录到历史
mUidRecorder.recordSnapshotLocked(delta, System.currentTimeMillis());
// 4. 检查流量配额
mPolicyListener.onStatsUpdated(delta);
}
}
应用层使用TrafficStats
// 查询应用流量
public class TrafficStatsExample {
public void showAppTraffic(int uid) {
// 1. 获取接收字节数
long rxBytes = TrafficStats.getUidRxBytes(uid);
// 2. 获取发送字节数
long txBytes = TrafficStats.getUidTxBytes(uid);
// 3. 获取接收数据包数
long rxPackets = TrafficStats.getUidRxPackets(uid);
// 4. 获取发送数据包数
long txPackets = TrafficStats.getUidTxPackets(uid);
Log.d(TAG, String.format(
"UID %d: RX=%s TX=%s",
uid,
humanReadableByteCount(rxBytes),
humanReadableByteCount(txBytes)
));
}
// 标记Socket (用于细粒度统计)
public void tagSocket(Socket socket, int tag) {
// 设置线程的流量标签
TrafficStats.setThreadStatsTag(tag);
try {
// 标记Socket
TrafficStats.tagSocket(socket);
// 进行网络操作...
socket.connect(address);
} finally {
// 取消标签
TrafficStats.untagSocket(socket);
TrafficStats.clearThreadStatsTag();
}
}
// 格式化流量显示
private String humanReadableByteCount(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
char pre = "KMGTPE".charAt(exp - 1);
return String.format("%.1f %sB",
bytes / Math.pow(1024, exp), pre);
}
}
1.4 按流量计费网络识别
Android需要识别哪些网络是按流量计费的(Metered Network),以便应用优化行为。
// NetworkCapabilities中的能力标识
public class NetworkCapabilities {
// 非计费网络(如家庭WiFi)
public static final int NET_CAPABILITY_NOT_METERED = 11;
// 临时非计费(如运营商免费流量)
public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
}
识别逻辑
// ConnectivityService.java
private void updateMeteredStatus(NetworkAgentInfo nai) {
NetworkCapabilities nc = nai.networkCapabilities;
// 1. WiFi默认非计费
if (nc.hasTransport(TRANSPORT_WIFI)) {
nc.addCapability(NET_CAPABILITY_NOT_METERED);
}
// 2. 移动网络默认计费
if (nc.hasTransport(TRANSPORT_CELLULAR)) {
nc.removeCapability(NET_CAPABILITY_NOT_METERED);
}
// 3. 但可以被配置覆盖
if (mNetworkPolicies.isNetworkMetered(nai)) {
nc.removeCapability(NET_CAPABILITY_NOT_METERED);
}
}
应用检查示例
public boolean isCurrentNetworkMetered(Context context) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
// 1. 获取当前网络
Network network = cm.getActiveNetwork();
if (network == null) return true; // 安全起见,假设计费
// 2. 获取能力
NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
if (capabilities == null) return true;
// 3. 检查是否非计费
return !capabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
}
1.5 Android 15新特性:eBPF网络策略
Android 15使用**eBPF (extended Berkeley Packet Filter)**替代传统的iptables,性能提升显著。
eBPF vs iptables对比
| 特性 | iptables | eBPF (Android 15) |
|---|---|---|
| 规则匹配 | 线性扫描 O(n) | 哈希查找 O(1) |
| 延迟 | ~100μs | ~10μs |
| 内核态切换 | 需要 | 不需要 |
| 动态更新 | 锁全表 | 无锁更新 |
| CPU占用 | 高 | 低 |
eBPF实现原理
// system/netd/bpf_progs/netd.c
// BPF程序 - UID流量控制
SEC("cgroupskb/ingress/stats")
int bpf_traffic_account(struct __sk_buff *skb) {
// 1. 获取UID
uint32_t uid = bpf_get_socket_uid(skb);
// 2. 查询UID规则
uint8_t *rule = bpf_uid_permission_map_lookup_elem(&uid);
// 3. 检查权限
if (rule && (*rule & BPF_PERMISSION_INTERNET) == 0) {
return 0; // 拒绝:丢弃数据包
}
// 4. 统计流量
struct stats_value *value = bpf_uid_stats_map_lookup_elem(&uid);
if (value) {
__sync_fetch_and_add(&value->rxBytes, skb->len);
__sync_fetch_and_add(&value->rxPackets, 1);
}
return 1; // 允许:继续传输
}
// BPF Map - UID权限表
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, uint32_t); // UID
__type(value, uint8_t); // 权限标志
__uint(max_entries, 10000);
} uid_permission_map SEC(".maps");
// BPF Map - UID流量统计
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, uint32_t); // UID
__type(value, struct stats_value);
__uint(max_entries, 10000);
} uid_stats_map SEC(".maps");
Java层接口
// frameworks/base/.../BpfNetMaps.java
public class BpfNetMaps {
// 设置UID网络规则
public void setUidRule(int uid, int rule) {
// 直接更新BPF Map
mUidPermissionMap.updateEntry(
new UidPermissionKey(uid),
new UidPermissionValue(rule)
);
}
// 读取UID流量统计
public NetworkStats readStatsForUid(int uid) {
UidStatsValue value = mUidStatsMap.getValue(
new UidStatsKey(uid)
);
return new NetworkStats.Entry(
null, uid,
SET_DEFAULT, TAG_NONE,
METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO,
value.rxBytes, value.rxPackets,
value.txBytes, value.txPackets,
0 /* operations */
);
}
}
性能提升:
测试场景: 1000个应用同时联网
iptables方式:
- 规则匹配时间: 1000 * 100μs = 100ms
- CPU占用: 15%
eBPF方式:
- 规则匹配时间: 1000 * 10μs = 10ms
- CPU占用: 3%
性能提升: 10倍延迟降低,5倍CPU减少
二、VPN框架深度解析
2.1 VpnService架构总览
VPN (Virtual Private Network) 虚拟专用网络,在Android中通过VpnService框架实现。
核心组件
┌─────────────────────────────────────────┐
│ Android VPN架构 │
├─────────────────────────────────────────┤
│ │
│ 应用层 │
│ ┌────────────────┐ │
│ │ VpnService │ ← 应用继承实现 │
│ │ (抽象基类) │ │
│ └────────────────┘ │
│ ↓ │
│ ┌────────────────┐ │
│ │ Builder │ ← 配置VPN参数 │
│ └────────────────┘ │
│ │
│ Framework层 │
│ ┌────────────────┐ │
│ │ Vpn.java │ ← VPN管理核心 │
│ └────────────────┘ │
│ ↓ │
│ ┌────────────────┐ │
│ │ConnectivitySrv │ ← 网络连接管理 │
│ └────────────────┘ │
│ │
│ Native层 │
│ ┌────────────────┐ │
│ │ Netd │ ← 路由配置 │
│ └────────────────┘ │
│ │
│ Kernel层 │
│ ┌────────────────┐ │
│ │ TUN Driver │ ← 虚拟网卡 │
│ │ /dev/tun │ │
│ └────────────────┘ │
│ │
└─────────────────────────────────────────┘
2.2 VpnService应用层实现
基础VPN示例
// 应用实现VPN服务
public class MyVpnService extends VpnService {
private ParcelFileDescriptor mInterface;
private Thread mVpnThread;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 1. 准备VPN (弹出用户授权对话框)
Intent prepare = VpnService.prepare(this);
if (prepare != null) {
// 需要用户授权
startActivity(prepare);
return START_NOT_STICKY;
}
// 2. 建立VPN连接
mVpnThread = new Thread(this::setupVpn);
mVpnThread.start();
return START_STICKY;
}
private void setupVpn() {
try {
// 3. 配置VPN参数
Builder builder = new Builder();
builder
// 设置VPN地址
.addAddress("10.0.0.2", 24)
// 设置路由 (所有流量通过VPN)
.addRoute("0.0.0.0", 0)
// 设置DNS
.addDnsServer("8.8.8.8")
.addDnsServer("8.8.4.4")
// 设置MTU
.setMtu(1500)
// 设置会话名称
.setSession("MyVPN")
// 配置允许/禁止的应用
.addAllowedApplication("com.example.app")
.addDisallowedApplication("com.example.excluded");
// 4. 建立TUN接口
mInterface = builder.establish();
if (mInterface == null) {
Log.e(TAG, "Failed to establish VPN");
return;
}
// 5. 获取文件描述符
FileDescriptor fd = mInterface.getFileDescriptor();
// 6. 读写TUN设备
handleTraffic(fd);
} catch (Exception e) {
Log.e(TAG, "VPN setup failed", e);
}
}
// 处理TUN设备流量
private void handleTraffic(FileDescriptor fd) {
FileInputStream in = new FileInputStream(fd);
FileOutputStream out = new FileOutputStream(fd);
byte[] packet = new byte[32767];
while (true) {
try {
// 1. 从TUN设备读取IP数据包
int length = in.read(packet);
if (length > 0) {
// 2. 解析IP包
IpPacket ipPacket = parseIpPacket(packet, length);
// 3. 加密/封装数据
byte[] encrypted = encrypt(ipPacket);
// 4. 发送到VPN服务器
sendToVpnServer(encrypted);
}
// 5. 从VPN服务器接收数据
byte[] received = receiveFromVpnServer();
if (received != null) {
// 6. 解密/解封装
byte[] decrypted = decrypt(received);
// 7. 写回TUN设备
out.write(decrypted);
}
} catch (IOException e) {
break;
}
}
}
@Override
public void onDestroy() {
if (mVpnThread != null) {
mVpnThread.interrupt();
}
if (mInterface != null) {
try {
mInterface.close();
} catch (IOException e) {
// Ignore
}
}
super.onDestroy();
}
}
VPN配置详解
public class VpnConfigExample {
// 配置1:全局VPN (所有流量)
public ParcelFileDescriptor setupGlobalVpn(VpnService service) {
return service.new Builder()
.addAddress("10.0.0.2", 24)
.addRoute("0.0.0.0", 0) // 所有IPv4流量
.addRoute("::", 0) // 所有IPv6流量
.addDnsServer("8.8.8.8")
.establish();
}
// 配置2:分流VPN (仅特定网段)
public ParcelFileDescriptor setupSplitVpn(VpnService service) {
return service.new Builder()
.addAddress("10.0.0.2", 24)
.addRoute("192.168.0.0", 16) // 仅企业内网
.addRoute("10.0.0.0", 8) // 仅私有网络
.addDnsServer("10.0.0.1")
.establish();
}
// 配置3:应用级VPN (仅特定应用)
public ParcelFileDescriptor setupPerAppVpn(VpnService service)
throws PackageManager.NameNotFoundException {
Builder builder = service.new Builder()
.addAddress("10.0.0.2", 24)
.addRoute("0.0.0.0", 0);
// 方式1:白名单模式 (只有指定应用走VPN)
builder.addAllowedApplication("com.example.secure");
builder.addAllowedApplication("com.example.banking");
// 方式2:黑名单模式 (指定应用不走VPN)
// builder.addDisallowedApplication("com.example.local");
return builder.establish();
}
// 配置4:Always-on VPN (系统级持久VPN)
public ParcelFileDescriptor setupAlwaysOnVpn(VpnService service) {
return service.new Builder()
.addAddress("10.0.0.2", 24)
.addRoute("0.0.0.0", 0)
.addDnsServer("8.8.8.8")
.setBlocking(true) // 阻塞模式:VPN断开时阻止所有流量
.establish();
}
}
2.3 Framework层VPN管理
源码位置:
packages/modules/Connectivity/service/src/com/android/server/connectivity/Vpn.java
Vpn.java核心实现
// Vpn.java (简化版)
public class Vpn {
// VPN配置
private VpnConfig mConfig;
// TUN接口名称
private String mInterface;
// VPN网络
private Network mNetwork;
// VPN NetworkAgent
private NetworkAgent mNetworkAgent;
// 核心方法:建立VPN
public synchronized ParcelFileDescriptor establish(VpnConfig config) {
// 1. 权限检查
enforceControlPermission();
// 2. 创建TUN设备
String interfaceName = jniCreate(config.mtu);
if (interfaceName == null) {
throw new IllegalStateException("Failed to create TUN device");
}
mInterface = interfaceName;
// 3. 配置TUN设备地址
for (LinkAddress address : config.addresses) {
jniAddAddress(interfaceName, address.getAddress(),
address.getPrefixLength());
}
// 4. 配置路由
Connection connection = new Connection();
connection.interfaceName = interfaceName;
connection.routes = config.routes;
// 5. 通知Netd配置路由
try {
mNetd.addVpnUidRanges(mInterface, config.allowedUidRanges);
for (RouteInfo route : config.routes) {
mNetd.vpnAddRoute(mInterface, route);
}
} catch (RemoteException e) {
throw new IllegalStateException("Netd error", e);
}
// 6. 设置DNS
try {
mNetd.setDnsServersForInterface(interfaceName,
config.dnsServers,
config.searchDomains);
} catch (RemoteException e) {
// ...
}
// 7. 创建NetworkAgent
agentConnect(config);
// 8. 打开TUN设备文件描述符
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(
jniGetFd(interfaceName)
);
return tun;
}
// 创建NetworkAgent
private void agentConnect(VpnConfig config) {
// 构建NetworkCapabilities
NetworkCapabilities caps = new NetworkCapabilities();
caps.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
caps.removeCapability(NET_CAPABILITY_NOT_VPN);
if (config.allowBypass) {
caps.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
// 构建LinkProperties
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(mInterface);
// 创建NetworkAgent
mNetworkAgent = new NetworkAgent(mContext, mLooper,
"VPN", caps, lp, 0, new NetworkAgentConfig(), this) {
@Override
public void onNetworkUnwanted() {
// VPN被移除
Vpn.this.onNetworkUnwanted();
}
};
mNetworkAgent.register();
mNetwork = mNetworkAgent.getNetwork();
}
// JNI方法 - 创建TUN设备
private native int jniCreate(int mtu);
private native String jniGetName(int tun);
private native void jniAddAddress(String interfaceName,
String address, int prefixLen);
private native int jniGetFd(String interfaceName);
}
JNI层TUN设备操作
// frameworks/base/core/jni/android_net_LocalSocketImpl.cpp
static jint com_android_server_connectivity_Vpn_jniCreate(JNIEnv* env,
jobject /* clazz */,
jint mtu) {
// 1. 打开TUN设备
int tun = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (tun < 0) {
jniThrowException(env, "java/io/IOException",
"Cannot open TUN device");
return -1;
}
// 2. 配置TUN设备
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; // TUN模式,无协议头
strncpy(ifr.ifr_name, "tun%d", IFNAMSIZ);
// 3. 创建TUN接口
if (ioctl(tun, TUNSETIFF, &ifr) < 0) {
close(tun);
jniThrowException(env, "java/io/IOException",
"Cannot configure TUN device");
return -1;
}
// 4. 设置MTU
if (mtu > 0 && mtu != ifr.ifr_mtu) {
ifr.ifr_mtu = mtu;
if (ioctl(tun, SIOCSIFMTU, &ifr) < 0) {
ALOGW("Cannot set MTU");
}
}
return tun;
}
// 添加IP地址到TUN接口
static void com_android_server_connectivity_Vpn_jniAddAddress(
JNIEnv* env, jobject /* clazz */,
jstring jInterfaceName, jstring jAddress, jint prefixLen) {
const char* interfaceName = env->GetStringUTFChars(jInterfaceName, NULL);
const char* address = env->GetStringUTFChars(jAddress, NULL);
// 使用netlink添加地址
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[256];
} req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_type = RTM_NEWADDR;
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
req.ifa.ifa_family = AF_INET; // 或AF_INET6
req.ifa.ifa_prefixlen = prefixLen;
req.ifa.ifa_index = if_nametoindex(interfaceName);
// 添加地址属性...
addattr_l(&req.n, sizeof(req), IFA_LOCAL, address, 4);
// 发送到内核...
send_netlink_message(&req.n);
env->ReleaseStringUTFChars(jInterfaceName, interfaceName);
env->ReleaseStringUTFChars(jAddress, address);
}
2.4 Always-on VPN
Always-on VPN是Android 7.0引入的功能,确保设备始终通过VPN连接。
工作机制
Always-on VPN开启后:
1. 系统启动时自动启动VPN
→ 无需用户手动连接
2. VPN断开时阻止所有网络访问
→ "Block connections without VPN"选项
3. VPN服务崩溃时自动重连
→ 由系统监控并重启
4. 不允许用户手动断开
→ 只有管理员可以关闭
配置Always-on VPN
// 系统设置 - 配置Always-on VPN
public class AlwaysOnVpnConfig {
// 设置Always-on VPN
public void setAlwaysOnVpn(Context context, String packageName,
boolean lockdownEnabled) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
// 需要CONTROL_ALWAYS_ON_VPN权限 (系统应用)
cm.setAlwaysOnVpnPackageForUser(
UserHandle.myUserId(),
packageName,
lockdownEnabled, // 是否阻断模式
null // 允许绕过的应用列表
);
}
// 检查Always-on VPN状态
public boolean isAlwaysOnVpnEnabled(Context context) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
String vpnPackage = cm.getAlwaysOnVpnPackageForUser(
UserHandle.myUserId()
);
return vpnPackage != null;
}
}
Framework实现
// Vpn.java
public boolean setAlwaysOnPackage(String packageName, boolean lockdown,
List<String> lockdownWhitelist) {
enforceControlPermissionOrInternalCaller();
if (packageName == null) {
// 关闭Always-on VPN
mAlwaysOn = false;
mLockdown = false;
return true;
}
// 验证VPN应用
if (!isVpnServicePreConsented(packageName)) {
return false;
}
// 保存配置
mAlwaysOn = true;
mLockdown = lockdown;
mLockdownWhitelist = lockdownWhitelist;
// 启动VPN
startAlwaysOnVpn();
return true;
}
private void startAlwaysOnVpn() {
// 1. 如果已连接,什么都不做
if (mNetworkAgent != null && mNetworkAgent.isConnected()) {
return;
}
// 2. 启动VPN服务
Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
intent.setPackage(mPackage);
intent.setComponent(mPackage, mService);
try {
mContext.startServiceAsUser(intent, UserHandle.of(mUserHandle));
} catch (RuntimeException e) {
Log.e(TAG, "Failed to start Always-on VPN", e);
}
// 3. 如果是锁定模式,配置防火墙
if (mLockdown) {
setVpnForcedLocked(true);
}
}
// 锁定模式:阻止所有非VPN流量
private void setVpnForcedLocked(boolean enforce) {
if (enforce) {
// 添加防火墙规则:拒绝所有UID的网络访问
// 除了VPN应用和白名单应用
mNetd.firewallSetUidRule(FIREWALL_CHAIN_VPN, UID_ALL, DENY);
// 允许VPN应用
mNetd.firewallSetUidRule(FIREWALL_CHAIN_VPN,
mOwnerUID, ALLOW);
// 允许白名单应用
for (String pkg : mLockdownWhitelist) {
int uid = getUidForPackage(pkg);
mNetd.firewallSetUidRule(FIREWALL_CHAIN_VPN, uid, ALLOW);
}
} else {
// 移除防火墙规则
mNetd.firewallSetUidRule(FIREWALL_CHAIN_VPN, UID_ALL, ALLOW);
}
}
三、网络性能优化实战
3.1 流量优化技巧
1. 压缩传输
public class NetworkOptimization {
// 使用GZIP压缩HTTP请求体
public void sendCompressedRequest(String url, String data)
throws IOException {
HttpURLConnection conn = (HttpURLConnection)
new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Encoding", "gzip");
// GZIP压缩
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gzos = new GZIPOutputStream(baos)) {
gzos.write(data.getBytes(StandardCharsets.UTF_8));
}
byte[] compressed = baos.toByteArray();
// 发送压缩数据
try (OutputStream os = conn.getOutputStream()) {
os.write(compressed);
}
// 日志压缩比
Log.d(TAG, String.format("Compression ratio: %.2f%%",
100.0 * compressed.length / data.length()));
}
// 使用Brotli压缩 (更高压缩率)
public void sendBrotliRequest(String url, byte[] data)
throws IOException {
// Brotli库: com.aayushatharva.brotli4j:brotli4j
BrotliCompressor compressor = new BrotliCompressor();
byte[] compressed = compressor.compress(data,
Brotli.DEFAULT_QUALITY, Brotli.DEFAULT_WINDOW);
HttpURLConnection conn = (HttpURLConnection)
new URL(url).openConnection();
conn.setRequestProperty("Content-Encoding", "br");
// 发送...
}
}
2. 图片优化
public class ImageOptimization {
// 根据网络状态调整图片质量
public void loadOptimizedImage(Context context, String url,
ImageView imageView) {
// 1. 检查网络类型
boolean isMetered = isCurrentNetworkMetered(context);
boolean isSlowNetwork = isSlowNetwork(context);
// 2. 选择质量
String imageUrl;
if (isSlowNetwork || isMetered) {
// 慢速/计费网络:低质量图片
imageUrl = url + "?quality=low&size=small";
} else {
// 快速/非计费网络:高质量图片
imageUrl = url + "?quality=high&size=large";
}
// 3. 加载图片 (使用Glide/Coil等库)
Glide.with(context)
.load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.into(imageView);
}
// 判断是否慢速网络
private boolean isSlowNetwork(Context context) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
NetworkCapabilities caps = cm.getNetworkCapabilities(
cm.getActiveNetwork());
if (caps == null) return true;
// 下行带宽 < 1Mbps 视为慢速
int downBandwidth = caps.getLinkDownstreamBandwidthKbps();
return downBandwidth < 1000;
}
}
3. 预取策略
public class PrefetchStrategy {
// 智能预取
public void smartPrefetch(Context context) {
// 仅在WiFi + 充电时预取
if (isWifiConnected(context) && isCharging(context)) {
// 预取下一章节内容
prefetchNextChapter();
// 预加载推荐列表
prefetchRecommendations();
// 同步离线数据
syncOfflineData();
}
}
private boolean isWifiConnected(Context context) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
NetworkCapabilities caps = cm.getNetworkCapabilities(
cm.getActiveNetwork());
return caps != null &&
caps.hasTransport(TRANSPORT_WIFI) &&
caps.hasCapability(NET_CAPABILITY_NOT_METERED);
}
private boolean isCharging(Context context) {
BatteryManager bm = context.getSystemService(
BatteryManager.class);
return bm.isCharging();
}
}
3.2 DNS优化
1. DNS缓存
public class DnsCache {
private final Map<String, InetAddress[]> mCache =
new ConcurrentHashMap<>();
// DNS查询带缓存
public InetAddress[] lookup(String hostname) throws UnknownHostException {
// 1. 检查缓存
InetAddress[] cached = mCache.get(hostname);
if (cached != null && !isExpired(cached)) {
return cached;
}
// 2. DNS查询
InetAddress[] addresses = InetAddress.getAllByName(hostname);
// 3. 缓存结果 (TTL 5分钟)
mCache.put(hostname, addresses);
scheduleExpiry(hostname, 5 * 60 * 1000);
return addresses;
}
// 预热DNS缓存
public void warmUpCache(String... hostnames) {
ExecutorService executor = Executors.newFixedThreadPool(4);
for (String hostname : hostnames) {
executor.submit(() -> {
try {
lookup(hostname);
} catch (UnknownHostException e) {
// Ignore
}
});
}
}
}
2. DoH (DNS over HTTPS)
public class DohResolver {
private static final String DOH_SERVER =
"https://dns.google/dns-query";
// 使用DoH查询DNS
public List<InetAddress> resolveDoH(String hostname)
throws IOException {
// 1. 构建DoH请求
String url = DOH_SERVER + "?name=" + hostname + "&type=A";
HttpsURLConnection conn = (HttpsURLConnection)
new URL(url).openConnection();
conn.setRequestProperty("Accept", "application/dns-json");
// 2. 发送请求
int responseCode = conn.getResponseCode();
if (responseCode != 200) {
throw new IOException("DoH query failed: " + responseCode);
}
// 3. 解析JSON响应
String response = readStream(conn.getInputStream());
JSONObject json = new JSONObject(response);
JSONArray answers = json.getJSONArray("Answer");
List<InetAddress> addresses = new ArrayList<>();
for (int i = 0; i < answers.length(); i++) {
JSONObject answer = answers.getJSONObject(i);
String ip = answer.getString("data");
addresses.add(InetAddress.getByName(ip));
}
return addresses;
}
}
3.3 连接池管理
public class ConnectionPoolOptimization {
// OkHttp连接池配置
public OkHttpClient createOptimizedClient() {
// 1. 配置连接池
ConnectionPool connectionPool = new ConnectionPool(
10, // 最大空闲连接数
5, // 连接保活时间
TimeUnit.MINUTES
);
// 2. 配置超时
return new OkHttpClient.Builder()
.connectionPool(connectionPool)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
// 3. 启用HTTP/2
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
// 4. 配置重试
.retryOnConnectionFailure(true)
// 5. 添加拦截器
.addInterceptor(new LoggingInterceptor())
.addNetworkInterceptor(new CacheInterceptor())
.build();
}
// 自定义缓存拦截器
private class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 离线时使用缓存
if (!isNetworkAvailable()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
// 自定义缓存策略
if (isNetworkAvailable()) {
// 在线:缓存1小时
response = response.newBuilder()
.header("Cache-Control",
"public, max-age=" + 60 * 60)
.build();
} else {
// 离线:缓存4周
response = response.newBuilder()
.header("Cache-Control",
"public, only-if-cached, max-stale=" + 60 * 60 * 24 * 28)
.build();
}
return response;
}
}
}
3.4 网络诊断工具
1. 网络质量监测
# 检查网络连接状态
adb shell dumpsys connectivity | grep "Active networks"
# 检查DNS配置
adb shell getprop net.dns1
adb shell getprop net.dns2
# 测试网络延迟
adb shell ping -c 4 8.8.8.8
# 测试DNS解析
adb shell nslookup www.google.com
# 检查路由表
adb shell ip route show
# 查看接口统计
adb shell ifconfig wlan0
# 抓取网络数据包
adb shell tcpdump -i wlan0 -w /sdcard/capture.pcap
# 查看iptables规则
adb shell iptables -L -n -v
# 查看eBPF统计 (Android 15)
adb shell dumpsys connectivity trafficcontroller
2. 流量监控
public class TrafficMonitor {
private long mLastRxBytes = 0;
private long mLastTxBytes = 0;
private long mLastTime = 0;
// 计算实时网速
public String getCurrentNetworkSpeed(int uid) {
long currentTime = System.currentTimeMillis();
long currentRx = TrafficStats.getUidRxBytes(uid);
long currentTx = TrafficStats.getUidTxBytes(uid);
if (mLastTime == 0) {
// 首次调用,初始化
mLastRxBytes = currentRx;
mLastTxBytes = currentTx;
mLastTime = currentTime;
return "0 KB/s";
}
// 计算时间间隔
long timeDelta = currentTime - mLastTime;
if (timeDelta == 0) return "0 KB/s";
// 计算流量增量
long rxDelta = currentRx - mLastRxBytes;
long txDelta = currentTx - mLastTxBytes;
// 计算速度 (字节/秒)
long rxSpeed = rxDelta * 1000 / timeDelta;
long txSpeed = txDelta * 1000 / timeDelta;
// 更新记录
mLastRxBytes = currentRx;
mLastTxBytes = currentTx;
mLastTime = currentTime;
return String.format("↓ %s/s ↑ %s/s",
formatBytes(rxSpeed),
formatBytes(txSpeed));
}
private String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return (bytes / 1024) + " KB";
return String.format("%.2f MB", bytes / 1024.0 / 1024.0);
}
}
四、Android 15新特性
4.1 网络切片支持(Network Slicing)
5G网络切片允许为不同业务提供差异化的网络服务。
// Android 15 - 请求企业专网切片
public void requestEnterpriseSlice(Context context) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_ENTERPRISE)
.setEnterpriseId(NET_ENTERPRISE_ID_1) // 企业切片ID
.build();
cm.requestNetwork(request, new NetworkCallback() {
@Override
public void onAvailable(Network network) {
// 使用企业专网切片
// - 低延迟 (<10ms)
// - 高带宽保证
// - QoS优先级高
bindProcessToNetwork(network);
}
});
}
4.2 卫星连接支持
// Android 15 - 检测卫星网络
public boolean isSatelliteNetwork(Context context) {
ConnectivityManager cm = context.getSystemService(
ConnectivityManager.class);
NetworkCapabilities caps = cm.getNetworkCapabilities(
cm.getActiveNetwork());
return caps != null &&
caps.hasTransport(TRANSPORT_SATELLITE);
}
4.3 eBPF性能提升
Android 15全面使用eBPF替代iptables:
- 流量统计:O(1)复杂度
- 防火墙规则:哈希查找,延迟降低90%
- CPU占用:减少60%
五、总结与展望
5.1 核心要点回顾
-
NetworkPolicy流量控制
- NetworkPolicyManagerService管理网络策略
- Data Saver省流模式
- 流量统计与限制
- eBPF性能优化
-
VPN框架
- VpnService应用层实现
- TUN虚拟网卡
- 路由配置与数据转发
- Always-on VPN机制
-
网络优化
- 压缩传输
- DNS缓存与DoH
- 连接池管理
- 流量监控工具
5.2 最佳实践建议
| 场景 | 建议 |
|---|---|
| 省流优化 | 监听Data Saver状态,动态调整资源质量 |
| VPN开发 | 使用TUN而非TAP,处理好异常重连 |
| 后台同步 | 使用WorkManager + 网络约束条件 |
| 大文件下载 | 分段下载,支持断点续传,WiFi优先 |
| 实时通信 | 使用WebSocket保持长连接,心跳保活 |
六、参考资源
6.1 源码路径
# NetworkPolicy
packages/modules/Connectivity/service/src/com/android/server/net/
├── NetworkPolicyManagerService.java
├── NetworkStatsService.java
└── NetworkPolicyManagerInternal.java
# VPN
packages/modules/Connectivity/service/src/com/android/server/connectivity/
├── Vpn.java
└── VpnNetworkObserver.java
# eBPF
system/netd/bpf_progs/
├── netd.c
└── bpf_net_helpers.h
6.2 调试命令
# 网络策略
adb shell dumpsys network_policy
# 流量统计
adb shell dumpsys netstats
# VPN状态
adb shell dumpsys connectivity | grep -A 20 "VPN"
# eBPF统计
adb shell dumpsys connectivity trafficcontroller
# 设置Data Saver
adb shell cmd netpolicy set restrict-background true
# 查看UID策略
adb shell cmd netpolicy list uid-policy
6.3 官方文档
后记
网络策略和VPN是Android网络子系统中最复杂的两个模块,它们共同保障了:
- 用户隐私:VPN加密保护数据传输
- 流量控制:防止后台应用偷跑流量
- 电量优化:限制不必要的网络活动
- 安全隔离:企业级网络访问控制
系列文章
欢迎来我中的个人主页找到更多有用的知识和有趣的产品