news 2026/7/18 4:09:08

Android 7系统网络(六)Framework层(下)—NMS_NPMS_NSS三大服务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android 7系统网络(六)Framework层(下)—NMS_NPMS_NSS三大服务

系列目录:第一篇:全景图与调用链路概览 | 第二篇:内核层—netfilter/iptables与路由策略 | 第三篇:Native层(上)—netd守护进程与CommandListener | 第四篇:Native层(下)—netd Controller详解 | 第五篇:Framework层(上)—ConnectivityService核心机制 |第六篇:Framework层(下)—NMS/NPMS/NSS三大服务| 第七篇:连接建立—WiFi/移动数据/以太网完整流程 | 第八篇:应用API层—ConnectivityManager使用与实战调试


一、为什么要了解这三大服务

第五篇讲到的ConnectivityService是总调度,但实际执行网络策略、统计流量、与 netd 通信的是它背后的三个服务。当你遇到以下问题时,答案就在它们之中:

  • “为什么开启了省电模式,某个应用还能用移动数据?”——NPMS的白名单可能没有覆盖到该应用
  • “为什么流量统计显示的数字和运营商账单对不上?”——NSS的统计周期或持久化文件可能有问题
  • “为什么 Framework 下发的防火墙规则在 netd 中没有生效?”——NMSNativeDaemonConnector通信链路可能断了

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,通过NetworkTemplatematches()方法匹配具体网络,支持通配符匹配(如"所有 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 流量周期与警告

warningByteslimitBytes分离——警告只是通知,不阻断网络;硬限制会直接禁用移动数据。NPMS通过NetworkPolicywarningByteslimitBytes字段驱动通知逻辑:

用户设置流量上限为 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.javaframeworks/base/services/core/java/com/android/server/netd 通信桥梁、防火墙规则、Observer 机制
NativeDaemonConnector.javaframeworks/base/services/core/java/com/android/server/Socket 通信、命令封装
INetworkManagementEventObserver.aidlframeworks/base/core/java/android/net/网络事件回调接口
NetworkPolicyManagerService.javaframeworks/base/services/core/java/com/android/server/net/省电模式、Doze、流量周期
NetworkPolicy.javaframeworks/base/core/java/android/net/网络策略数据结构
NetworkStatsService.javaframeworks/base/services/core/java/com/android/server/net/流量统计、持久化
NetworkStats.javaframeworks/base/core/java/android/net/流量统计条目
NetworkTemplate.javaframeworks/base/core/java/android/net/网络匹配模板

七、小结

三大服务各有侧重,协同工作:

服务核心职责关键交互
NMSnetd 通信桥梁NativeDaemonConnector+INetworkManagementEventObserver
NPMS网络策略管理省电/Doze/流量周期
NSS流量统计xt_qtaguid+ 持久化

下一篇将进入网络代理层,逐一拆解 WiFi、移动数据、以太网三种网络的完整建立流程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 4:08:43

TockOS与Arduino Nano 33 BLE Sense的嵌入式开发实践

1. 项目概述&#xff1a;当TockOS遇上Arduino Nano 33 BLE Sense在嵌入式开发领域&#xff0c;点灯&#xff08;Blinky&#xff09;就像程序员的"Hello World"——简单却意义重大。这次我们要在Arduino Nano 33 BLE Sense这块功能强大的开发板上&#xff0c;运行Tock…

作者头像 李华
网站建设 2026/7/18 4:08:36

新手买电钢琴必看:5大避坑要点+10款实测推荐,看完秒懂选琴

新手选购电钢琴时&#xff0c;最容易在键盘手感、音色质量和功能配置上踩坑。结合市场口碑与核心配置标准&#xff0c;以下为您总结了选购电钢琴的5大避坑要点&#xff0c;并精选了10款高性价比电钢琴&#xff08;包含3款派德拉机型&#xff09;供您参考。一、 选购电钢琴的5大…

作者头像 李华
网站建设 2026/7/18 4:08:09

接了8个大模型API之后,我花一周时间算清了这笔AI账

我是个产品负责人&#xff0c;不是技术出身。但过去半年&#xff0c;因为业务需要&#xff0c;我被迫成了一个"大模型API半个专家"。 起因很简单&#xff1a;老板让我评估一下公司的AI应用成本&#xff0c;看看有没有优化空间。我心想这不就是看看账单嘛&#xff0c…

作者头像 李华
网站建设 2026/7/18 4:07:43

汽车行业数据治理:挑战、痛点与解决方案

1. 汽车行业数据治理现状与挑战汽车行业正经历着从传统制造业向数字化服务商的转型。随着智能网联、自动驾驶、共享出行等新业态的发展&#xff0c;车企每天产生的数据量呈指数级增长。一辆智能网联汽车每天可产生超过4TB的数据&#xff0c;包括车辆运行数据、用户行为数据、环…

作者头像 李华
网站建设 2026/7/18 4:06:49

Git 基本使用指南:从入门到掌握核心操作

1. Git 简介 Git 是一个分布式版本控制系统&#xff0c;由 Linus Torvalds 于 2005 年为管理 Linux 内核开发而创建。它能够高效地处理从小型到大型项目的版本管理&#xff0c;跟踪代码变更&#xff0c;并支持多人协作开发。 与集中式版本控制系统&#xff08;如 SVN&#xf…

作者头像 李华