系列目录:第一篇:全景图与调用链路概览 | 第二篇:内核层—netfilter/iptables与路由策略 | 第三篇:Native层(上)—netd守护进程与CommandListener | 第四篇:Native层(下)—netd Controller详解 | 第五篇:Framework层(上)—ConnectivityService核心机制 |第六篇:Framework层(下)—NMS/NPMS/NSS三大服务| 第七篇:连接建立—WiFi/移动数据/以太网完整流程 | 第八篇:应用API层—ConnectivityManager使用与实战调试
一、为什么要了解这三大服务
第五篇讲到的ConnectivityService是总调度,但实际执行网络策略、统计流量、与 netd 通信的是它背后的三个服务。当你遇到以下问题时,答案就在它们之中:
- “为什么开启了省电模式,某个应用还能用移动数据?”——
NPMS的白名单可能没有覆盖到该应用 - “为什么流量统计显示的数字和运营商账单对不上?”——
NSS的统计周期或持久化文件可能有问题 - “为什么 Framework 下发的防火墙规则在 netd 中没有生效?”——
NMS的NativeDaemonConnector通信链路可能断了
NMS、NPMS、NSS 三者通过ConnectivityService协调,形成"执行-统计-决策"的闭环:
┌───────────────────────────────────────────────────┐ │ ConnectivityService (总调度) │ │ 网络评分、切换、默认网络管理 │ └───────────────────────────────────────────────────┘ │ │ │ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │ NMS │ │ NPMS │ │ NSS │ │ netd通信 │ │ 网络策略 │ │ 流量统计 │ │ 桥梁 │ │ 省电/限制 │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ ▼ ▼ ▼ netd FirewallCtrl xt_qtaguid关键:NMS 负责执行,NSS 负责统计,NPMS 负责决策,三者通过
ConnectivityService协调形成闭环。
二、NetworkManagementService (NMS)
2.1 定位与职责
NMS 是 Framework 层与 netd 通信的唯一桥梁,封装 netd 命令并提供 Java 接口。它的核心职责包括管理网络接口、设置防火墙规则、管理带宽配额、监控网络事件。
2.2 与 netd 的通信桥梁:NativeDaemonConnector
NMS 不直接操作 Socket,而是通过NativeDaemonConnector与 netd 交互。
源码路径:frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java
finalclassNativeDaemonConnectorimplementsRunnable,Handler.Callback,Watchdog.Monitor{privateStringmSocket;// 与 netd 的 Socket 路径privateOutputStreammOutputStream;// 发送命令privateLocalLogmLocalLog;// 本地日志privateHandlermCallbackHandler;// 回调分发privateINativeDaemonConnectorCallbacksmCallbacks;// 响应回调privatefinalResponseQueuemResponseQueue;// 响应队列privatestaticfinallongDEFAULT_TIMEOUT=1*60*1000;// 默认超时 1 分钟// ...// 入口:可变参数格式,最常用publicNativeDaemonEventexecute(Stringcmd,Object...args)throwsNativeDaemonConnectorException{returnexecute(DEFAULT_TIMEOUT,cmd,args);}// 带超时版本publicNativeDaemonEventexecute(longtimeoutMs,Stringcmd,Object...args)throwsNativeDaemonConnectorException{finalNativeDaemonEvent[]events=executeForList(timeoutMs,cmd,args);if(events.length!=1){thrownewNativeDaemonConnectorException("Expected exactly one response, but received "+events.length);}returnevents[0];← 期望单条响应,多条则抛异常}}关键设计:
execute最终委托给executeForList,后者负责实际的 Socket 通信——将命令序列化为字符串发送、读取响应、解析为NativeDaemonEvent数组。execute校验返回数组长度必须为 1,确保单条命令→单条响应的严格对应。
关键设计:
NativeDaemonConnector实现Watchdog.Monitor接口,保证与 netd 的通信链路被系统健康监控覆盖——一旦通信线程阻塞超过阈值,Watchdog 会触发系统重启。
通信过程:
NMS 调用 mConnector.execute("firewall", "set_uid_rule", ...) ↓ NativeDaemonConnector 通过 LocalSocket 发送字符串命令 ↓ netd 的 CommandListener 接收并处理 ↓ NativeDaemonConnector 读取响应,由 NativeDaemonEvent 解析 ↓ 解析响应码(200 系列成功 / 400/500 系列错误) ↓ 返回给 NMS关键:netd 的响应格式为
<code> <message>,由NativeDaemonEvent解析,200 系列表示成功,400/500 系列表示错误。
2.3 NMS 的核心方法
NMS 每个方法内部都通过mConnector.execute()向 netd 发送命令,方法签名与 netd 支持的命令一一对应。
源码路径:frameworks/base/services/core/java/com/android/server/NetworkManagementService.java
publicclassNetworkManagementServiceextendsINetworkManagementService.Stub{// ...privatefinalNativeDaemonConnectormConnector;// 与 netd 通信的连接器// —— 接口管理 ——publicString[]listInterfaces();// 列出所有接口publicInterfaceConfigurationgetInterfaceConfig(Stringiface);// 获取接口配置publicvoidsetInterfaceConfig(Stringiface,InterfaceConfigurationcfg);// 设置接口配置publicvoidsetInterfaceDown(Stringiface);// 关闭接口publicvoidsetInterfaceUp(Stringiface);// 启用接口publicvoidaddRoute(intnetId,RouteInforoute);// 添加路由publicvoidremoveRoute(intnetId,RouteInforoute);// 删除路由// —— 防火墙 ——publicvoidsetFirewallEnabled(booleanenabled);// 启用防火墙publicvoidsetFirewallUidRule(intchain,intuid,intrule);// 设置 UID 规则publicvoidsetFirewallInterfaceRule(Stringiface,booleanallow);// 设置接口规则// —— 带宽控制 ——publicvoidsetInterfaceQuota(Stringiface,longquotaBytes);// 设置接口配额publicvoidremoveInterfaceQuota(Stringiface);// 移除接口配额publicvoidsetGlobalAlert(longalertBytes);// 设置全局警告// —— 网络共享 ——publicvoidtetherInterface(Stringiface);// 添加共享接口publicvoiduntetherInterface(Stringiface);// 移除共享接口publicString[]listTetheredInterfaces();// 列出生效的共享接口publicvoidstartTethering(String[]dhcpRange);// 启动共享publicvoidstopTethering();// 停止共享// —— DNS ——publicvoidsetDnsServersForNetwork(intnetId,String[]servers,Stringdomains);// 设置 DNS}2.4 NMS 的命令封装模式
每条 NMS 方法都封装成对应的 netd 命令。以setFirewallUidRule为例:
publicclassNetworkManagementServiceextendsINetworkManagementService.Stub{// ...privatefinalNativeDaemonConnectormConnector;// 与 netd 通信的连接器privatefinalObjectmQuotaLock=newObject();// 配额操作锁publicvoidsetFirewallUidRule(intchain,intuid,intrule){enforceSystemUid();// 强制调用者必须是系统 UIDsynchronized(mQuotaLock){setFirewallUidRuleLocked(chain,uid,rule);}}privatevoidsetFirewallUidRuleLocked(intchain,intuid,intrule){if(updateFirewallUidRuleLocked(chain,uid,rule)){mConnector.execute("firewall","set_uid_rule",getFirewallChainName(chain),uid,getFirewallRuleName(chain,rule));}}}关键设计:
enforceSystemUid()强制调用者必须是系统 UID,防止第三方应用直接操作防火墙规则。
关键设计:同步锁
mQuotaLock保证防火墙规则与配额操作互斥,setFirewallUidRuleLocked内部通过mConnector.execute("firewall", "set_uid_rule", ...)将命令下发给 netd。
2.5 Observer 机制
NMS 通过 AIDL 定义的INetworkManagementEventObserver接口,让上层服务监听网络事件。Observer 定义在 AIDL 文件中,跨进程通信——netd 通过NativeDaemonConnector回调到 NMS,NMS 再通过 AIDL 回调通知上层服务。
源码路径:frameworks/base/core/java/android/net/INetworkManagementEventObserver.aidl
interfaceINetworkManagementEventObserver{voidinterfaceStatusChanged(Stringiface,booleanup);// 接口状态变化voidinterfaceLinkStateChanged(Stringiface,booleanup);// 链路状态变化voidinterfaceAdded(Stringiface);// 接口新增voidinterfaceRemoved(Stringiface);// 接口移除voidaddressUpdated(Stringiface,inLinkAddressaddress);// 地址更新voidaddressRemoved(Stringiface,inLinkAddressaddress);// 地址移除voidlimitReached(StringlimitName,Stringiface);// 达到带宽限制voidinterfaceClassDataActivityChanged(Stringlabel,booleanactive,longtsNanos);// 数据活动voidinterfaceDnsServerInfo(Stringiface,longlifetime,inString[]servers);// DNS 信息voidrouteUpdated(inRouteInforoute);// 路由更新voidrouteRemoved(inRouteInforoute);// 路由移除}netd 通过NetlinkManager检测到内核事件后,会向所有已连接的 NMS Observer 发送通知:
内核:接口 wlan0 UP ↓ netd NetlinkManager 收到 RTM_NEWLINK ↓ 通知 NativeDaemonConnector(通过 Socket) ↓ NMS 收到 "600 Iface change" 消息 ↓ NMS 遍历所有 Observer ↓ ConnectivityService 收到 interfaceStatusChanged("wlan0", true)关键:600 系列响应码是 netd 主动推送的异步事件通知,与 200/400/500 系列的命令响应不同——NMS 通过
onEvent()回调处理异步事件。
三、NetworkPolicyManagerService (NPMS)
3.1 定位与职责
NPMS 管理系统的网络策略,核心功能包括省电模式、流量周期、应用白名单、Doze 模式。
3.2 策略的数据结构
NetworkPolicy定义了网络策略的核心字段:流量周期、警告阈值、硬限制、是否计费网络等。
源码路径:frameworks/base/core/java/android/net/NetworkPolicy.java
publicclassNetworkPolicyimplementsParcelable,Comparable<NetworkPolicy>{publicstaticfinalintCYCLE_NONE=-1;// 无周期publicstaticfinallongWARNING_DISABLED=-1;publicstaticfinallongLIMIT_DISABLED=-1;publicstaticfinallongSNOOZE_NEVER=-1;publicNetworkTemplatetemplate;// 模板(匹配规则)publicintcycleDay;// 计费周期日publicStringcycleTimezone;// 时区publiclongwarningBytes;// 警告阈值publiclonglimitBytes;// 硬限制publiclonglastWarningSnooze;// 上次警告推迟时间publiclonglastLimitSnooze;// 上次限制推迟时间publicbooleanmetered;// 是否计费网络publicbooleaninferred;// 是否推断出来的策略}关键设计:
template字段的类型是NetworkTemplate而非int,通过NetworkTemplate的matches()方法匹配具体网络,支持通配符匹配(如"所有 WiFi 网络")。
3.3 省电模式实现
省电模式的核心逻辑是限制后台应用使用移动数据。
源码路径:frameworks/base/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
publicclassNetworkPolicyManagerServiceextendsINetworkPolicyManager.Stub{// ...privatestaticfinalintFIREWALL_CHAIN_POWERSAVE=1;// 省电模式防火墙链privatevoidupdateRulesForGlobalChangeAL(booleanrestrictedNetworksChanged){// restrictedNetworksChanged 为 true 时:// 遍历所有非白名单应用,将其加入 fw_powersave 链// 同步白名单到 NMS,确保白名单应用不受限制}}省电模式生效流程:
用户开启省电模式 ↓ NPMS.updateRulesForGlobalChangeAL(true) ↓ 遍历所有非白名单应用 ↓ NMS.setFirewallUidRule(CHAIN_POWERSAVE, uid, DENY) ↓ 向 netd 发送: ↓ firewall set_uid_rule powersave <uid> deny ↓ netd FirewallController 执行 iptables 规则: iptables -I fw_powersave -o rmnet_data0 -m owner --uid-owner <uid> -j REJECT ↓ 该 UID 的应用发送移动数据 → 被 iptables REJECT关键:省电模式只限制移动数据(
-o rmnet_data0),WiFi 不受影响,确保用户在 WiFi 环境下仍可正常使用。
白名单有三个来源:系统预置白名单来自SystemConfig(/system/etc/permissions/下的 XML 配置),用户手动添加的白名单持久化在/data/system/deviceidle.xml,临时白名单仅存于内存。NPMS通过DeviceIdleController.getAppIdWhitelist()获取白名单,再调用updateRulesForWhitelistedPowerSaveUL同步到NMS,确保白名单应用不受防火墙规则限制。
3.4 Doze 模式与网络限制
Doze 模式比省电模式更严格——完全禁止后台应用的网络访问(不仅是移动数据),只在设备长时间不使用时激活,定期打开"维护窗口"让应用同步数据。
publicclassNetworkPolicyManagerServiceextendsINetworkPolicyManager.Stub{// ...privatebooleanmDeviceIdleMode;// Doze 模式标志voidupdateRulesForDeviceIdleUL(){// deviceIdle == true 时:// 所有非白名单应用加入 fw_dozable 链,禁止所有网络访问(包括 WiFi)updateRulesForWhitelistedPowerSaveUL(mDeviceIdleMode,FIREWALL_CHAIN_DOZABLE,mUidFirewallDozableRules);}}关键:Doze 模式下
fw_dozable链同时作用于 INPUT 和 OUTPUT,但 ICMPv6 邻居发现协议(RS/RA/NS/NA)会被放行,避免 IPv6 连接完全中断。
3.5 流量周期与警告
warningBytes和limitBytes分离——警告只是通知,不阻断网络;硬限制会直接禁用移动数据。NPMS通过NetworkPolicy的warningBytes和limitBytes字段驱动通知逻辑:
用户设置流量上限为 1GB ↓ NPMS 创建/更新 NetworkPolicy (limitBytes = 1GB) ↓ 每次 NSS 更新流量数据后通知 NPMS ↓ NPMS 调用 NSS.getNetworkStats(mobileTemplate) ↓ 获取移动数据总流量 ↓ 比较 total vs warning / limit ├── total < warning (900MB) → 正常 ├── total >= warning → 发送警告通知 └── total >= limit (1GB) → 禁用移动数据四、NetworkStatsService (NSS)
4.1 定位与职责
NSS 负责统计系统的网络流量,是 NPMS 的"眼睛"——NPMS 需要流量数据来判断是否达到警告或限制阈值。
4.2 数据源
NSS 从内核xt_qtaguid模块收集流量数据。xt_qtaguid在内核中为每个 Socket 创建唯一的 tag,所有数据包在协议栈中自动统计。
来源1:xt_qtaguid(内核统计)
内核 xt_qtaguid 模块 → /proc/net/xt_qtaguid/stats ↓ NetworkStatsFactory 定期读取 ↓ 解析为 NetworkStats 对象每条统计记录由NetworkStats.Entry承载:
源码路径:frameworks/base/core/java/android/net/NetworkStats.java
publicclassNetworkStatsimplementsParcelable{publicstaticclassEntry{publicStringiface;// 接口名(wlan0 / rmnet_data0)publicintuid;// 应用 UIDpublicintset;// DEFAULT / FOREGROUND / BACKGROUNDpublicinttag;// 流量标记(0x0 或应用的标记)publicintroaming;// 是否漫游publiclongrxBytes;// 接收字节数publiclongrxPackets;// 接收数据包数publiclongtxBytes;// 发送字节数publiclongtxPackets;// 发送数据包数publiclongoperations;// 操作计数}}关键设计:
set字段区分前台(FOREGROUND)和后台(BACKGROUND)流量,让 NPMS 可以分别控制前后台的数据使用策略。
关键设计:
tag字段支持应用通过TrafficStats.setThreadStatsTag()自定义标记,实现 Socket 级别的精细化流量追踪。
来源2:当应用调用TrafficStats.setThreadStatsTag()时,NSS 也会记录这些标记统计。
4.3 统计周期与持久化
NSS 按多个周期统计流量,三种 bucket 粒度不同——设备级只关心接口总量,UID 级按应用区分,UID+Tag 级最细粒度。
源码路径:frameworks/base/services/core/java/com/android/server/net/NetworkStatsService.java
publicclassNetworkStatsServiceextendsINetworkStatsService.Stub{// ...// 统计周期配置(从 Settings.Global 读取)// NETSTATS_DEV_BUCKET_DURATION — 设备级,默认 1 小时// NETSTATS_UID_BUCKET_DURATION — UID 级,默认 2 小时// NETSTATS_UID_TAG_BUCKET_DURATION — UID+Tag 级,默认 2 小时// 持久化目录privatestaticfinalStringPREFIX_XT="xt";// 与 "/data/system/netstats/" 组合// 实际持久化文件:// /data/system/netstats/netstats.bin — 设备级// /data/system/netstats/netstats_xt.bin — 按 UID+Tag// /data/system/netstats/netstats_uid.bin — 按 UID}数据持久化流程:
NSS 定期从内核读取流量数据 ↓ 与已有数据合并(accumulate) ↓ 写入 /data/system/netstats/netstats*.bin ↓ NPMS 读取该文件判断是否达到限制关键:持久化使用二进制格式,重启后可以恢复统计数据,避免因重启丢失流量记录。
4.4 NetworkTemplate 匹配机制
NSS 通过NetworkTemplate来匹配特定网络的流量。
源码路径:frameworks/base/core/java/android/net/NetworkTemplate.java
publicclassNetworkTemplate{// 匹配所有 WiFi 网络publicstaticNetworkTemplatebuildTemplateWifiWildcard();// 匹配所有移动数据网络(按 IMSI)publicstaticNetworkTemplatebuildTemplateMobileAll(StringsubscriberId);// 匹配所有移动数据网络(通配符,不区分运营商)publicstaticNetworkTemplatebuildTemplateMobileWildcard();// 匹配以太网publicstaticNetworkTemplatebuildTemplateEthernet();}这样 NPMS 可以查询特定网络的流量,getNetworkStats()返回的是累计值,NPMS 需要自行计算周期内的增量。
// 查询移动数据本月流量NetworkTemplatemobileTemplate=NetworkTemplate.buildTemplateMobileAll(subscriberId);NetworkStatsstats=mNetworkStatsService.getNetworkStats(mobileTemplate);longtotalBytes=stats.getTotalBytes();4.5 与 NPMS 的协作
用户设置流量上限为 1GB ↓ NPMS 创建 NetworkPolicy (limitBytes = 1GB) ↓ 每次 NSS 更新流量数据后通知 NPMS ↓ NPMS 调用 NSS.getNetworkStats(mobileTemplate) ↓ 获取移动数据总流量 ↓ 比较 total vs limit ├── total < warning (900MB) → 正常 ├── total >= warning → 发送警告通知 └── total >= limit (1GB) → 禁用移动数据关键:NSS 和 NPMS 通过
INetworkStatsService/INetworkPolicyManager的 AIDL 接口通信,两者相互独立,通过ConnectivityService协调。
五、三大服务协作全景 — 省电+流量限制 完整链路
用一个完整的"省电 + 流量限制"场景串联三大服务:
用户开启省电模式 + 设置了 1GB 流量上限 ↓ ┌──────────────────────────────────────────┐ │ NPMS:策略管理层 │ │ ├── 监听省电模式开关 │ │ ├── 加载 NetworkPolicy (1GB 上限) │ │ └── 决定限制哪些应用的网络 │ └──────────┬──────────┬─────────────────────┘ │ │ ┌──────▼──────┐ │ │ NSS │ │ │ 提供流量 │ │ │ 统计数据 │←──┘ └─────────────┘ │ NPMS 调用 NMS │ ┌──────────▼─────────────────────────────────┐ │ NMS:命令执行层 │ │ ├── setFirewallUidRule(powersave, uid, deny) │ │ └── setInterfaceQuota(rmnet0, 1GB) │ └──────────┬─────────────────────────────────┘ │ NativeDaemonConnector ▼ ┌──────────────────────────────────────────┐ │ netd Controller 层 │ │ FirewallController → iptables 规则 │ │ BandwidthController → xt_qtaguid 配额 │ └──────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────┐ │ 内核层 │ │ netfilter/iptables 拦截包 │ │ xt_qtaguid 统计流量 │ └──────────────────────────────────────────┘关键:NPMS 是决策层(“限制谁”),NSS 是感知层(“用了多少”),NMS 是执行层(“怎么限制”),三者通过
ConnectivityService协调完成完整的网络策略闭环。
六、关键源码文件索引
| 文件 | 路径 | 本文涉及内容 |
|---|---|---|
NetworkManagementService.java | frameworks/base/services/core/java/com/android/server/ | netd 通信桥梁、防火墙规则、Observer 机制 |
NativeDaemonConnector.java | frameworks/base/services/core/java/com/android/server/ | Socket 通信、命令封装 |
INetworkManagementEventObserver.aidl | frameworks/base/core/java/android/net/ | 网络事件回调接口 |
NetworkPolicyManagerService.java | frameworks/base/services/core/java/com/android/server/net/ | 省电模式、Doze、流量周期 |
NetworkPolicy.java | frameworks/base/core/java/android/net/ | 网络策略数据结构 |
NetworkStatsService.java | frameworks/base/services/core/java/com/android/server/net/ | 流量统计、持久化 |
NetworkStats.java | frameworks/base/core/java/android/net/ | 流量统计条目 |
NetworkTemplate.java | frameworks/base/core/java/android/net/ | 网络匹配模板 |
七、小结
三大服务各有侧重,协同工作:
| 服务 | 核心职责 | 关键交互 |
|---|---|---|
| NMS | netd 通信桥梁 | NativeDaemonConnector+INetworkManagementEventObserver |
| NPMS | 网络策略管理 | 省电/Doze/流量周期 |
| NSS | 流量统计 | xt_qtaguid+ 持久化 |
下一篇将进入网络代理层,逐一拆解 WiFi、移动数据、以太网三种网络的完整建立流程。