Android 15网络子系统深度解析(一):ConnectivityService与网络管理框架全解析

4 阅读50分钟

1、ConnectivityService 架构总览

1.1 核心职责与设计理念

ConnectivityService 的五大核心职责
═════════════════════════════════════════════════════════════

1️⃣ 网络监控与发现(Network Monitoring & Discovery)
────────────────────────────────────────────────────────────

核心功能:
├─ 监听所有网络接口的状态变化
├─ 检测新网络的出现(WiFiLTEEthernet 等)
├─ 维护 NetworkAgentInfo 列表(所有已知网络)
├─ 跟踪网络的连接/断开事件

实现方式:
├─ 通过 INetworkAgent AIDL 接口接收来自网络提供者的更新
├─ NetworkAgent 在网络状态变化时调用:
  ├─ markConnected() - 标记为连接
  ├─ unregister() - 标记为断开
  └─ sendNetworkCapabilities/LinkProperties - 发送能力更新
└─ ConnectivityService 维护网络列表并及时响应

数据结构:
```java
// 所有已注册的网络代理
private List<NetworkAgentInfo> mNetworkAgents = new ArrayList<>();

// 网络评分(用于选择最佳网络)
private Map<Network, NetworkScore> mNetworkScores = new HashMap<>();

// 网络状态跟踪
private Map<Network, NetworkInfo> mNetworkInfos = new HashMap<>();

2️⃣ 网络请求匹配与路由(Network Request Matching & Routing) ─────────────────────────────────────────────────────────────

核心功能: ├─ 管理应用的网络请求(NetworkRequest) ├─ 匹配网络与请求的关系 ├─ 为应用分配最合适的网络 ├─ 处理网络切换逻辑

请求类型: ├─ LISTEN 请求:监听特定网络(不使用) ├─ REQUEST 请求:要求特定网络(使用) ├─ TRACK_DEFAULT 请求:跟踪默认网络变化 └─ LISTEN_FOR_BEST 请求:监听最好的网络

匹配算法:

private void rematchAllNetworksAndRequests() {
    // 步骤 1:获取所有网络,按评分排序(从高到低)
    List<NetworkAgentInfo> sorted = sortNetworksByScore(mNetworkAgents);
    
    // 步骤 2:对于每个网络,找出满足条件的所有请求
    for (NetworkAgentInfo nai : sorted) {
        for (NetworkRequestInfo nri : mNetworkRequests) {
            if (networkSatisfiesRequest(nai, nri.request)) {
                // 步骤 3:为请求分配网络
                allocateNetworkForRequest(nai, nri);
            }
        }
    }
    
    // 步骤 4:通知应用有新网络可用
    notifyNetworkCallbacks();
}

网络匹配的核心逻辑(SimplifyNetworkCapabilities):

private boolean networkSatisfiesRequest(
    NetworkAgentInfo nai, NetworkRequest request) {
    
    NetworkCapabilities caps = nai.networkCapabilities;
    
    // 检查 REQUIRED 能力(必须满足)
    for (int reqCap : request.getCapabilities()) {
        if (!caps.hasCapability(reqCap)) {
            return false;  // ❌ 网络不满足要求
        }
    }
    
    // 检查 UNWANTED 能力(不能有)
    for (int unwantedCap : request.getUnwantedCapabilities()) {
        if (caps.hasCapability(unwantedCap)) {
            return false;  // ❌ 网络有不需要的能力
        }
    }
    
    // 如果指定了特定传输方式
    if (request.hasTransportType()) {
        if (!caps.hasTransport(request.getTransport())) {
            return false;  // ❌ 传输方式不匹配
        }
    }
    
    return true;  // ✓ 网络满足所有要求
}

3️⃣ 网络状态上报与广播(Network State Reporting & Broadcasting) ──────────────────────────────────────────────────────────────

核心功能: ├─ 收集网络状态信息(连接、断开、能力变化等) ├─ 通知应用网络变化(通过 NetworkCallback) ├─ 发送 CONNECTIVITY_ACTION 广播给系统 ├─ 更新默认网络

网络状态变化的上报流程:

NetworkAgent 状态变化
        ↓
ConnectivityService.handleNetworkInfoChange()
        ↓
notifyNetworkCallbacks(nai, callbackType)
        ↓
通知所有监听该网络的 NetworkCallback
        ↓
App 收到回调
    ├─ onAvailable(Network)
    ├─ onCapabilitiesChanged(Network, caps)
    ├─ onLinkPropertiesChanged(Network, lp)
    └─ onLost(Network)

所有支持的回调类型(CallbackType):

// Framework 能力相关
CALLBACK_PRECHECK           // 网络预检查(验证前)
CALLBACK_AVAILABLE          // 网络可用
CALLBACK_LOSING             // 网络即将丧失(Linger 状态)
CALLBACK_LOST               // 网络已丧失
CALLBACK_UNAVAILABLE        // 网络不可用

// 能力与属性变化
CALLBACK_CAPABILITIES_CHANGED     // 能力变化
CALLBACK_LINK_PROPERTIES_CHANGED  // 链路属性变化
CALLBACK_IP_CHANGED               // IP 地址变化

// 验证相关
CALLBACK_NETWORK_STATE_CHANGED    // 网络验证状态变化
CALLBACK_PROVISIONING_CHANGED     // 配置状态变化

// 高级回调
CALLBACK_BLK_CHANGED              // 阻塞状态变化
CALLBACK_SUSPENDED                // 网络暂停
CALLBACK_RESUMED                  // 网络恢复

4️⃣ 网络验证与评估(Network Validation & Assessment) ───────────────────────────────────────────────────

核心功能: ├─ 确定网络是否有真正的互联网连接 ├─ 检测是否存在强制门户(Captive Portal) ├─ 评估网络的连通性质量 ├─ 根据验证结果更新网络能力

验证过程(3 个阶段):

阶段 1: 网络连接
├─ NetworkAgent 调用 markConnected()
├─ 网络标记为已连接(LOCAL_NETWORK 除外)
└─ 如果网络满足 INTERNET 要求,进入验证

阶段 2: NetworkMonitor 探测
├─ 发送 HTTP 请求到 captive portal 检测服务器
├─ 检查 HTTP 重定向(强制门户)
├─ 发送 DNS 查询(DNS 可达性)
├─ 验证 HTTPS(私有 DNS)
└─ 根据结果返回验证状态

阶段 3: 更新 NET_CAPABILITY_VALIDATED
├─ 如果所有探测通过  添加 NET_CAPABILITY_VALIDATED
├─ 如果存在强制门户  添加 NET_CAPABILITY_CAPTIVE_PORTAL
└─ 如果只有部

分连通  添加 NET_CAPABILITY_PARTIAL_CONNECTIVITY

验证时间线:

T=0: 网络连接
     └─ NetworkAgent.markConnected() 被调用

T=1: ConnectivityService 创建网络
     ├─ 通知 NetworkMonitor 开始验证
     └─ 如果网络满足 INTERNET 要求

T=2-5s: NetworkMonitor 探测
     ├─ HTTP GET 到 Google 的探测服务器
     ├─ DNS 查询 Google 域名
     └─ HTTPS 连接测试

T=5s: 验证完成
     ├─ 更新 NET_CAPABILITY_VALIDATED
     ├─ 将网络标记为"就绪"
     └─ 应用可以使用该网络

验证结果(6 种):

1. NETWORK_VALIDATION_RESULT_VALID
   └─ 网络可以访问互联网,没有强制门户
   └─ 添加 NET_CAPABILITY_VALIDATED

2. NETWORK_VALIDATION_RESULT_PARTIAL
   └─ 网络有部分连通(例如只有 IPv4 或 IPv6)
   └─ 添加 NET_CAPABILITY_PARTIAL_CONNECTIVITY

3. NETWORK_VALIDATION_RESULT_SKIPPED
   └─ 验证被跳过(例如 LOCAL_NETWORK)
   └─ 不添加任何验证能力

4. NETWORK_VALIDATION_RESULT_PORTAL_DETECTED
   └─ 检测到强制门户
   └─ 添加 NET_CAPABILITY_CAPTIVE_PORTAL

5. NETWORK_VALIDATION_RESULT_OFFLINE_DETECTED
   └─ 网络完全离线(不能访问任何外部资源)
   └─ 不添加任何验证能力

6. NETWORK_VALIDATION_RESULT_INVALID
   └─ 验证失败(未知原因)
   └─ 不添加任何验证能力

5️⃣ 网络策略执行与流量控制(Network Policy & Traffic Control) ──────────────────────────────────────────────────────────────

核心功能: ├─ 实施后台应用限制 ├─ 控制流量计量(Metered) ├─ 数据使用限制与警告 ├─ 省流量模式(Data Saver) ├─ App 权限与网络访问控制

流量控制层级:

Layer 1: 全局策略
├─ Background Restriction(后台限制)
├─ Data Saver Mode(省流量)
├─ Doze Mode(打瞌睡模式)
└─ Power Save Mode(省电)

        
Layer 2: UID 级限制
├─ 应用是否在后台运行
├─ 应用是否被冻结
├─ 应用是否被锁定
└─ 应用的权限(INTERNET, ACCESS_NETWORK_STATE 等)

        
Layer 3: 网络级限制
├─ 网络是否被计量(METERED)
├─ 网络是否被漫游(ROAMING)
├─ 网络是否受限(RESTRICTED)
└─ 网络的 VPN 状态

        
Layer 4: BPF 防火墙执行
├─ eBPF 在内核网络栈中拦截数据包
├─ 根据 UID + Network + Policy 决定是否转发
└─ 实时生效,无延迟

数据流与网络访问决策:

private void updateNetworkBlockStatus(int uid, Network network) {
    // 步骤 1: 检查应用是否有 INTERNET 权限
    if (!hasPermission(uid, Manifest.permission.INTERNET)) {
        block(uid, network);
        return;
    }
    
    // 步骤 2: 检查应用是否在后台
    if (isUidInBackground(uid)) {
        if (mRestrictBackgroundEnabled) {
            block(uid, network);
            return;
        }
    }
    
    // 步骤 3: 检查网络是否计量
    if (isNetworkMetered(network)) {
        if (isDataSaverEnabled() && !isWhitelisted(uid)) {
            block(uid, network);
            return;
        }
    }
    
    // 步骤 4: 检查 VPN 和特殊角色
    if (isNetworkVPN(network)) {
        // VPN 流量通常有特殊处理
    }
    
    // 步骤 5: 通过 BPF 防火墙执行
    applyFirewallRule(uid, network, ALLOW);
}

#### **1.2 与其他系统服务的关系**

【ConnectivityService 的依赖关系】 ═════════════════════════════════════════════════════════════

      ┌─────────────────────────────────┐
      │      SystemServer               │
      │ (启动所有系统服务的入口)        │
      └────────────────┬────────────────┘
                       │
       ┌───────────────┴────────────────┐
       │                                │
       ▼                                ▼

┌─────────────────────────┐ ┌──────────────────────┐ │ ConnectivityService │◄───┤ NetworkManagementSvc │ │ │ │ (Netd 接口) │ │ 核心网络管理服务 │ │ │ │ - 网络监控 │ └──────────────────────┘ │ - 请求匹配 │ △ │ - 路由决策 │ │ │ - 策略执行 │ INetd 接口 └────┬────────────────────┘ │ │ (Linux netlink socket) │ │ ├─────────────┬────────────────────┘ │ │ ▼ ▼ ┌──────────────────────────────────┐ ┌─────────────────┐ │ NetworkMonitor │ │ DnsResolver │ │ (网络验证) │ │ (DNS 解析) │ │ - 检测强制门户 │ │ │ │ - 验证互联网连通性 │ │ 与 NetworkMonitor │ - 检测网络质量 │ │ 协作验证网络 │ └──────────────────────────────────┘ └─────────────────┘

┌──────────────────────────────┐ ┌──────────────────────┐ │ WifiManager / WiFiService │ │ TelephonyManager │ │ (WiFi 网络代理) │ │ (移动网络代理) │ │ - 注册 WifiNetworkAgent │ │ - 注册 Cellular │ │ - 报告 WiFi 状态 │ │ NetworkAgent │ │ - WiFi 扫描与连接 │ │ - 报告蜂窝状态 │ │ │ │ - 信号强度更新 │ └──────────────────────────────┘ └──────────────────────┘

 │                                      │
 └──────────────┬───────────────────────┘
                │
     NetworkAgent 接口 (AIDL)
                │
  ┌─────────────┴──────────────┐
  │                            │
  ▼                            ▼

┌──────────────────┐ ┌──────────────────┐ │ VpnManager │ │ EthernetManager │ │ (VPN 代理) │ │ (以太网代理) │ │ │ │ │ │ - VPN 连接 │ │ - 有线网络 │ │ - 应用 VPN 路由 │ │ - 企业网络 │ └──────────────────┘ └──────────────────┘


关键交互流:

【网络请求的完整处理流程】 ════════════════════════════════════════════════════════

应用层 ├─ App 调用 ConnectivityManager.requestNetwork() └─ 发送 NetworkRequest 到 ConnectivityService

ConnectivityService (主线程 Handler) ├─ 接收 NetworkRequest(通过 Binder) ├─ 创建 NetworkRequestInfo ├─ 调用 rematchAllNetworksAndRequests() └─ 将请求发送给 NetworkFactory

NetworkFactory(通常在 WifiService 或 TelephonyManager) ├─ 接收请求 ├─ 检查是否可以满足(信号强度、资源可用性) ├─ 如果可以:启动网络连接流程 ├─ 如果不能:等待条件改善或拒绝 └─ 创建 NetworkAgent

NetworkAgent(新网络) ├─ 调用 register() 向 ConnectivityService 注册 ├─ 初始状态:CONNECTING ├─ 待网络就绪:调用 markConnected() ├─ 状态更新:调用 sendNetworkCapabilities(), sendNetworkScore() 等 └─ 网络断开:调用 unregister()

ConnectivityService 对 NetworkAgent 的响应 ├─ onNetworkInfoChanged():网络状态变化 ├─ onNetworkCapabilitiesChanged():能力变化 ├─ onNetworkScoreChanged():评分变化 ├─ 触发重新匹配:rematchAllNetworksAndRequests() ├─ 发送验证请求:发送给 NetworkMonitor └─ 广播网络变化:notifyNetworkCallbacks()

NetworkMonitor(验证网络) ├─ 接收网络信息 ├─ 发起 HTTP/HTTPS 探测 ├─ 查询 DNS ├─ 返回验证结果:VALID, PORTAL, OFFLINE 等 └─ ConnectivityService 更新网络能力

最终回调给应用 ├─ 通过 NetworkCallback 接口 ├─ onAvailable(Network) ├─ onCapabilitiesChanged() ├─ 应用可以开始使用网络 └─ 应用 getActiveNetwork() 返回该网络


#### **1.3 Android 15 的架构演进**

【Android 14 → Android 15 的主要变化】 ═════════════════════════════════════════════════════════════

Feature 1: 更精细的网络状态跟踪 ──────────────────────────────────

Android 14: ├─ 网络状态:CONNECTED / DISCONNECTED ├─ 验证状态:VALIDATED / UNVALIDATED └─ 基本的评分机制

Android 15: ├─ 网络状态:CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED ├─ Linger 状态:网络断开前可保活一段时间 ├─ 验证状态细分: │ ├─ VALIDATED(有效的互联网连接) │ ├─ PARTIAL(部分连通,例如仅 IPv4) │ ├─ CAPTIVE_PORTAL(强制门户) │ ├─ PROVISIONING(配置中) │ └─ OFFLINE(离线) ├─ 更详细的网络能力: │ ├─ OEM_PAID(OEM 付费网络) │ ├─ OEM_PRIVATE(OEM 私有网络) │ └─ LOCAL_NETWORK(本地网络,不需要互联网) └─ NetworkScore 更复杂的评分算法

Feature 2: LocalNetwork 支持 ──────────────────────────

问题:应用需要连接局域网设备(打印机、NAS 等),但不需要互联网

Android 14: ├─ LocalNetwork 概念有限 ├─ 本地网络无法与互联网网络共存 └─ 应用需要特殊处理

Android 15: ├─ 完整的 LocalNetwork 框架 ├─ LocalNetworkConfig 定义本地网络属性 ├─ 应用可以: │ ├─ 使用互联网网络(默认) │ ├─ 同时使用本地网络 │ ├─ 在两者之间切换 │ └─ 列举本地网络设备 ├─ DNS 解析支持: │ ├─ 局域网 mDNS(.local 域名) │ ├─ 企业 DNS(Active Directory) │ └─ 云端 DNS(AWS, Azure 等) └─ 应用无需 INTERNET 权限也能访问本地资源

Feature 3: 改进的网络验证机制 ────────────────────────────

Android 14: ├─ 简单的 HTTP 重定向检测 ├─ 缺少某些场景下的验证支持 └─ 验证失败处理不够细致

Android 15: ├─ 更智能的门户检测: │ ├─ 支持多种门户类型(Wifi 热点、航班 WiFi) │ ├─ 更好的假阳性(false positive)避免 │ └─ 支持跳过已知的良好网络验证 ├─ 隐私 DNS 验证: │ ├─ 支持 DNS-over-HTTPS (DoH) │ ├─ 支持 DNS-over-TLS (DoT) │ └─ 支持私有 DNS 转发 ├─ 部分连通性处理: │ ├─ IPv4-only 网络(某些运营商) │ ├─ IPv6-only 网络(新兴运营商) │ └─ 混合 Dual-stack 检测 └─ 验证缓存: └─ 相同 SSID/BSSID 的 WiFi 在一小时内无需重复验证

Feature 4: NetworkScore 演进 ─────────────────────────────

Android 14: ├─ 基本评分(-100 到 +100) ├─ 考虑因素: │ ├─ 网络类型(WiFi > LTE > 2G) │ ├─ 信号强度 │ ├─ 是否计量 │ └─ 是否验证 └─ 简单的排序算法

Android 15: ├─ 新的评分范围:0 到任意值(无上限) ├─ 考虑因素增加: │ ├─ 带宽(MBps) │ ├─ 延迟(毫秒) │ ├─ 丢包率(百分比) │ ├─ 网络稳定性(连接持续时间) │ ├─ 地理位置(优先本地网络) │ └─ 运营商优先级设置(OEM 配置) ├─ 更细致的决策: │ ├─ WiFi 5GHz vs 2.4GHz(不同评分) │ ├─ WiFi 加密强度(不同评分) │ ├─ LTE vs 5G(动态评分) │ └─ 不同 SIM 卡的评分(多卡设备) └─ 机器学习预测: └─ 基于历史数据预测网络质量(测试中)

Feature 5: 多网络并行使用 ──────────────────────────

Android 14: ├─ 多个应用使用不同网络(通过 NetworkRequest) ├─ 但默认网络通常是单一的(WiFi 或 LTE) └─ 应用很难同时使用多个网络

Android 15: ├─ 完整的多网络栈支持: │ ├─ 应用可以同时使用互联网网络 + 本地网络 │ ├─ 应用可以同时使用 WiFi + 移动网络(部分应用) │ └─ VPN 可以与其他网络共存 ├─ 改进的 DNS 处理: │ ├─ 支持多个 DNS 搜索列表 │ ├─ 根据域名选择 DNS 服务器 │ └─ 优化 DNS 重试(减少延迟) ├─ 路由策略(策略路由): │ ├─ 基于目标地址选择网络 │ ├─ 基于应用 UID 选择网络 │ ├─ 基于流量优先级选择网络 │ └─ VPN 流量优先级处理 └─ 应用 API: └─ 新的 NetworkCapabilities 和 NetworkRequest 选项

Feature 6: 改进的后台限制 ─────────────────────────

Android 14: ├─ 全局后台限制 ├─ 应用白名单机制 └─ 限制粒度较粗

Android 15: ├─ 更细致的限制: │ ├─ 按功能限制(定位、后台 Sync 等) │ ├─ 不同应用角色的不同限制 │ └─ 与设备模式关联(省电模式时更严格) ├─ 更聪明的检测: │ ├─ 检测应用是否在前台(基于 Visible Window) │ ├─ 检测是否有用户交互 │ └─ 检测优先级标签(PRIORITY, NORMAL, LOW) ├─ 改进的白名单: │ ├─ 动态白名单(基于条件) │ ├─ 时间表白名单(特定时间段) │ └─ 隐私感知白名单(权限相关) └─ 用户透明度: └─ Settings 中可见的网络限制原因

Feature 7: 安全与隐私增强 ─────────────────────────

Android 14: ├─ 基本的权限检查 ├─ MAC 地址隐藏(WiFi) └─ 有限的加密支持

Android 15: ├─ 增强的权限模型: │ ├─ 细粒度权限(CHANGE_NETWORK_STATE 分离) │ ├─ 权限委托(应用A 可代表应用B 请求网络) │ └─ 权限检查审计日志 ├─ 更好的隐私: │ ├─ 隐机制 MAC 地址(随机化间隔) │ ├─ MAC 地址池管理 │ ├─ MAC 地址与 SSID 绑定 │ └─ 防止 MAC 地址追踪 ├─ 加密网络管理: │ ├─ WPA3 完整支持 │ ├─ OWE(Open With Encryption) │ ├─ 企业 EAP 改进 │ └─ IoT 设备网络隔离 └─ 恶意网络防护: ├─ 检测已知的恶意 SSID ├─ 中间人攻击防护(HSTS) └─ DNS 劫持检测


---

### **2、NetworkAgent 机制深度解析**

#### **2.1 NetworkAgent 生命周期**

【完整的 NetworkAgent 生命周期】 ═════════════════════════════════════════════════════════════

状态图: ┌─────────────────────────────────────────────────────────┐ │ NetworkAgent 生命周期 │ └─────────────────────────────────────────────────────────┘

     ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓
     ┃ 1. UNREGISTERED (初始化) ┃
     ┃ - 创建 NetworkAgent       ┃
     ┃ - 配置初始能力和属性      ┃
     ┃ - 尚未向系统注册          ┃
     ┗━━━┯━━━━━━━━━━━━━━━━━━━━┛
         │
         │ register()
         │
     ┏━━━▼━━━━━━━━━━━━━━━━━━━━┓
     ┃ 2. REGISTERED (已注册)   ┃
     ┃ - 向 ConnectivityService ┃
     ┃   注册                   ┃
     ┃ - 收到 onRegistered()    ┃
     ┃ - 初始状态:DISCONNECTED ┃
     ┗━━━┯━━━━━━━━━━━━━━━━━━━━┛
         │
         │ (网络连接过程)
         │ - 发起连接
         │ - 发送能力/属性更新
         │
     ┏━━━▼━━━━━━━━━━━━━━━━━━━━┓
     ┃ 3. CONNECTING (连接中)   ┃
     ┃ - 网络处于连接过程        ┃
     ┃ - 向 ConnectivityService  ┃
     ┃   发送实时更新            ┃
     ┃ - 信号强度、IP 地址等可能 ┃
     ┃   不完整                  ┃
     ┗━━━┯━━━━━━━━━━━━━━━━━━━━┛
         │
         │ markConnected()
         │ - 标记网络已连接
         │ - 完整 IP 配置
         │ - 可以接收流量
         │
     ┏━━━▼━━━━━━━━━━━━━━━━━━━━┓
     ┃ 4. CONNECTED (已连接)    ┃
     ┃ - L3 连通性已建立         ┃
     ┃ - 应用可以使用            ┃
     ┃ - 可以验证互联网连通性    ┃
     ┃ - NetworkMonitor 可开始   ┃
     ┃   验证工作                ┃
     ┗━━━┯━━━┯━━━━━━━━━━━━━━┛
         │   │
         │   │ (网络更新)
         │   ├─ sendNetworkCapabilities()
         │   ├─ sendLinkProperties()
         │   ├─ sendNetworkScore()
         │   └─ (重复循环)
         │
         │ (网络断开或被替换)
         │
     ┌───┴──────────────────────┐
     │                          │
┏━━━▼━━━━┓         ┏━━━━━━━━━▼━━┓
┃ 5a. 正常断开   ┃ 5b. 被替换  ┃
┃ unregister()   ┃ unregisterAfter
┃               ┃ Replacement()
┗━━━┯━━━━┛         ┗━━━┯━━━━━┛
    │                 │
    │ (网络清理)      │ (网络保活)
    │ - 释放资源      │ - 仍满足旧请求
    │ - 断开网络      │ - 但新的同类网络优先
    │ - 停止验证      │ - 在超时或替换完成时断开
    │                 │
┏━━━▼━━━━━━━━━━━━━━━▼━━┓
┃ 6. DISCONNECTED (已断开) ┃
┃ - 网络不再可用           ┃
┃ - 资源已释放             ┃
┃ - 应用失去该网络         ┃
┃ - onDisconnected() 被调用 ┃
┃ - Agent 不可再使用        ┃
┗━━━━━━━━━━━━━━━━━━━━━━┛

详细阶段说明:

```javaStage 1: 初始化 (Initialization)】
─────────────────────────────────

// WiFi 网络代理示例
WifiNetworkAgent agent = new WifiNetworkAgent(
    context,
    looper,
    "WifiAgent-2.4GHz",  // 日志标签
    
    // 初始网络能力
    new NetworkCapabilities.Builder()
        .addTransportType(TRANSPORT_WIFI)
        .addCapability(NET_CAPABILITY_INTERNET)
        .addCapability(NET_CAPABILITY_NOT_METERED)
        .addCapability(NET_CAPABILITY_NOT_ROAMING)
        .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
        .build(),
    
    // 初始链路属性
    new LinkProperties.Builder()
        .setInterfaceName("wlan0")
        .addLinkAddress("192.168.1.100/24")
        .addRoute(new RouteInfo(null, "192.168.1.1"))
        .addDnsServer(InetAddress.getByName("8.8.8.8"))
        .build(),
    
    // 初始评分
    new NetworkScore.Builder()
        .setTransportPrimary(true)
        .setSignalStrength(85)  // WiFi 信号强度 (0-100)
        .build(),
    
    // 配置
    config,
    provider
);

// 在这个阶段,Agent 已创建但尚未注册
// 网络还不被系统知道Stage 2: 注册 (Registration)】
─────────────────────────────────

// 调用 register() 向 ConnectivityService 注册
agent.register();

→ ConnectivityService 处理流程:
├─ 创建 NetworkAgentInfo 包装 Agent
├─ 添加到 mNetworkAgents 列表
├─ 分配唯一的 netId
├─ 发送 onRegistered() 回调给 Agent
└─ 触发网络重匹配(rematchAllNetworksAndRequests// 在 register() 中,Agent 初始状态是 DISCONNECTED
// 这意味着网络还在连接过程中Stage 3: 连接 (Connecting)】
─────────────────────────────

// 网络提供者开始建立连接
// 例如 WiFi 模块执行以下操作:

step 1: 扫描和选择 AP
├─ WifiManager.startScan()
├─ 列出可用 SSID
└─ 选择最好的 BSSID

step 2: 发起连接
├─ 调用 WifiManager.connect()
├─ 发送 Association RequestAP
├─ AP 验证证书(如需要)
└─ 建立 802.11 连接

step 3: 获取 IP 地址
├─ 发送 DHCP Discovery
├─ 接收 DHCP Offer
├─ 发送 DHCP Request
└─ 获得 IP 地址,TTL 等信息

step 4: 向 ConnectivityService 报告进展
├─ 调用 sendNetworkCapabilities()
│  └─ 更新能力(例如加入 IPv6 能力)
├─ 调用 sendLinkProperties()
│  └─ 更新 IP 地址、DNSGateway 等
├─ 调用 sendNetworkScore()
│  └─ 更新信号强度得分
└─ 重复这些调用,直到完全就绪

// 在这个阶段,网络是 CONNECTING
// ConnectivityService 知道这个网络正在建立
// 但应用尚不能使用Stage 4: 已连接 (Connected)】
─────────────────────────────

// 当网络完全就绪时,调用 markConnected()

agent.markConnected();

→ ConnectivityService 处理流程:
├─ 更新网络状态为 CONNECTED
├─ 检查网络是否需要验证(是否满足 INTERNET 要求)
├─ 如需要,向 NetworkMonitor 发送验证请求
├─ 更新 mNetworkAgents 中的状态
├─ 触发网络重匹配
└─ 应用可能开始接收这个网络的回调

// 此时应用可以:
├─ 通过 NetworkRequest 请求该网络
├─ 使用 Network.getSocketFactory() 获取套接字
├─ 将流量定向到该网络
└─ 接收该网络的 CALLBACK_AVAILABLE

// 网络仍在验证中(如果需要)
// 网络可能没有 NET_CAPABILITY_VALIDATED 能力

好的,我继续深入讲解 Android 15 网络子系统。

现在让我继续详细讲解:

好的,继续讲解。现在我进入第 2-4 部分的详细讲解:


2.2 网络状态上报流程与回调机制

【NetworkAgent 上报网络状态的完整流程】
═════════════════════════════════════════════════════════════

核心概念:
├─ NetworkAgent 作为"信使"向 ConnectivityService 上报网络状态
├─ 通过 INetworkAgentRegistry AIDL 接口发送更新
├─ ConnectivityService 在专用线程处理这些更新
└─ 最终通过 NetworkCallback 通知应用

时序与调用链:

T=0: 网络事件发生 (例如获得 IP 地址)
     └─ WiFi Driver / Telephony 检测到事件

T=1: NetworkAgent 中的事件处理
     ├─ WiFiNetworkAgent.onNetworkStateChange()
     │  └─ 调用 sendNetworkCapabilities()
     │
     ├─ sendNetworkCapabilities() 实现:
     │  ├─ 创建新的 NetworkCapabilities 对象
     │  ├─ 通过 queueOrSendMessage() 添加到消息队列
     │  └─ AsyncResult.forMessage(mMessage)
     │
     └─ queueOrSendMessage() 内部:
        ├─ 检查是否可以立即发送(通常在同一线程)
        ├─ 或将消息排队到 mRegistry Messenger
        └─ 最后通过 Binder IPC 发送

T=2: IPC 传输 (Binder)
     └─ AIDL 参数序列化
        ├─ NetworkCapabilities 被 Parcel 化
        └─ 通过 Binder 内核驱动传输

T=3: ConnectivityService 接收 (Handler Thread)
     ├─ Messenger 中的 Handler 收到消息
     ├─ 调用 NetworkAgentMessageHandler.handleMessage()
     ├─ 根据消息类型分派:
     │  ├─ ASYNCCHANNEL_CMD_SUBSCRIBE_REPLY
     │  ├─ REQUEST_NETWORK
     │  ├─ RELEASE_NETWORK
     │  ├─ NETWORK_INFO_CHANGED
     │  ├─ NETWORK_CAPABILITIES_CHANGED
     │  ├─ LINK_PROPERTIES_CHANGED
     │  └─ ...
     └─ 调用对应的处理方法

T=4: ConnectivityService 处理状态变化
     ├─ 更新 NetworkAgentInfo 中的状态
     ├─ 可能触发网络重匹配
     ├─ 验证网络(如需要)
     └─ 广播通知所有监听者

T=5: 应用回调 (通过 NetworkCallback)
     ├─ 同步 Handler 线程中
     ├─ 或异步通过 Binder 回调
     └─ 应用收到通知:
        ├─ onAvailable()
        ├─ onCapabilitiesChanged()
        ├─ onLinkPropertiesChanged()
        └─ ...


【4 种关键的状态上报方法】
═════════════════════════════════════════════════════════════

1️⃣ sendNetworkCapabilities() - 能力变化
────────────────────────────────────

用途:
├─ 上报网络的功能能力
├─ 例如:支持 IPv6、计量、验证状态等
├─ 在网络连接的任何阶段可调用

示例代码:
```java
public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
    // 参数验证
    if (networkCapabilities == null) {
        throw new NullPointerException("networkCapabilities is null");
    }
    
    // 构建新的能力对象
    NetworkCapabilities newCaps = new NetworkCapabilities.Builder(networkCapabilities)
        // 可选:添加或移除能力
        .build();
    
    // 发送到 ConnectivityService
    // 内部调用:queueOrSendMessage(mNetworkCapabilitiesCallback, newCaps);
    queueOrSendMessage(reg -> reg.sendNetworkCapabilities(newCaps));
}

常见的能力变化: ├─ 获得 IPv6:addCapability(NET_CAPABILITY_IPV6) ├─ 网络验证:addCapability(NET_CAPABILITY_VALIDATED) ├─ 检测门户:addCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ├─ 信号变化:setSignalStrength(85) └─ 连通性部分:addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)

ConnectivityService 的处理:

private void handleNetworkCapabilitiesChanged(
    NetworkAgentInfo nai, NetworkCapabilities caps) {
    
    // 检查是否真的发生了变化
    NetworkCapabilities oldCaps = nai.networkCapabilities;
    if (oldCaps.equals(caps)) {
        return;  // 无变化,无需处理
    }
    
    // 记录日志(用于调试)
    logNetworkEvent(nai, "Capabilities changed", caps);
    
    // 更新网络的能力
    nai.setNetworkCapabilities(caps);
    
    // 重新评估网络的适用性(网络可能失去某些要求的能力)
    rematchAllNetworksAndRequests();
    
    // 通知所有监听者
    notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAPABILITIES_CHANGED);
    
    // 验证状态变化需要特殊处理
    if (oldCaps.hasCapability(NET_CAPABILITY_VALIDATED) 
        != caps.hasCapability(NET_CAPABILITY_VALIDATED)) {
        // 验证状态改变,更新 NetworkMonitor
        updateNetworkMonitorForValidation(nai);
    }
}

2️⃣ sendLinkProperties() - 链路属性变化 ────────────────────────────────────

用途: ├─ 上报网络的 L3 属性(链路层以上) ├─ IP 地址、子网掩码、网关、DNS 等 ├─ 路由信息、接口名称

示例代码:

public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
    if (linkProperties == null) {
        throw new NullPointerException("linkProperties is null");
    }
    
    // 典型场景:获得 IP 地址后更新
    LinkProperties newProps = new LinkProperties.Builder(linkProperties)
        .setInterfaceName("wlan0")                // 接口名
        .addLinkAddress("192.168.1.100/24")      // IP 和子网掩码
        .addRoute(new RouteInfo(null, "192.168.1.1"))  // 默认网关
        .addDnsServer(InetAddress.getByName("8.8.8.8")) // DNS 服务器
        .build();
    
    queueOrSendMessage(reg -> reg.sendLinkProperties(newProps));
}

ConnectivityService 的处理:

private void handleLinkPropertiesChanged(
    NetworkAgentInfo nai, LinkProperties lp) {
    
    LinkProperties oldLp = nai.linkProperties;
    if (oldLp.equals(lp)) {
        return;  // 无变化
    }
    
    // 更新 DNS 信息(系统 DNS 解析器)
    updateDnsServers(nai, lp);
    
    // 更新路由表(netd)
    updateRouting(nai, lp);
    
    // 更新 IP 地址(VPN、6to4 转换等)
    updateIpv6Configuration(nai, lp);
    
    // 通知应用
    notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LINK_PROPERTIES_CHANGED);
    
    // 如果接口名改变,更新 BPF 防火墙规则
    if (!oldLp.getInterfaceName().equals(lp.getInterfaceName())) {
        updateBpfRules(nai);
    }
}

IP 地址变化的影响: ├─ 应用收到 CALLBACK_IP_CHANGED ├─ Socket 连接可能需要重建 ├─ DNS 查询可能使用新的 DNS 服务器 └─ 路由表改变影响流量转向

3️⃣ sendNetworkScore() - 网络评分 ────────────────────────────────

用途: ├─ 告知系统网络质量(好坏) ├─ 影响网络选择(决定使用哪个网络) ├─ 动态更新(网络质量实时变化)

示例代码:

public void sendNetworkScore(@NonNull NetworkScore score) {
    if (score == null) {
        throw new NullPointerException("score is null");
    }
    
    // 评分通常基于信号强度
    // WiFi: 0-100 (RSSI 强度)
    // LTE: 0-100 (RSRP 强度)
    
    NetworkScore newScore = new NetworkScore.Builder()
        .setTransportPrimary(true)      // 是否是主要传输方式
        .setSignalStrength(rssiValue)   // 信号强度
        .setExiting(false)              // 是否正在退出(被替换)
        .build();
    
    queueOrSendMessage(reg -> reg.sendNetworkScore(newScore));
}

评分如何影响网络选择(SimpleNetworkScorer):

private int scoreDiff(NetworkScore score1, NetworkScore score2) {
    // 比较两个网络的评分
    // 返回正数:score1 更好
    // 返回负数:score2 更好
    // 返回 0:相同质量
    
    // WiFi 通常评分高于 LTE
    // 5GHz WiFi 高于 2.4GHz WiFi
    // 信号强的网络高于信号弱的网络
    
    // 如果 score1 是 WiFi,score2 是 LTE:
    // → score1 通常赢(假设两者都连接了)
    
    // 如果都是 WiFi,但信号不同:
    // → 信号强的赢 (80dbm > 70dbm)
}

// NetworkRanker 使用这个评分决定默认网络
private Network selectBestNetwork(List<NetworkAgentInfo> networks) {
    // 按评分从高到低排序
    Collections.sort(networks, (a, b) -> {
        return b.getScore() - a.getScore();
    });
    
    // 选择评分最高的
    if (!networks.isEmpty()) {
        return networks.get(0).network;
    }
    return null;
}

典型的评分变化场景:

WiFi 信号从 -60dbm 变为 -75dbm(变弱)
  → sendNetworkScore(new NetworkScore(..., 75))  (假设 75 分)
  → ConnectivityService 重新评估
  → 如果评分低于 LTE,可能切换到 LTE

用户靠近 WiFi 路由器,信号变强 -50dbm
  → sendNetworkScore(new NetworkScore(..., 95))  (95 分)
  → 切换回 WiFi(评分更高)

4️⃣ markConnected() - 标记网络已连接 ───────────────────────────────

用途: ├─ 表示 L3 连通性已建立 ├─ IP 地址、网关、DNS 都已配置 ├─ 网络可以接受流量

方法签名:

public void markConnected() {
    // 这是一个标志性方法,表示网络已准备就绪
    // 之前的 CONNECTING 状态 → 现在的 CONNECTED 状态
    queueOrSendMessage(reg -> reg.markConnected());
}

ConnectivityService 的处理:

private void handleMarkConnected(NetworkAgentInfo nai) {
    if (nai.getNetworkState() == NetworkInfo.State.CONNECTED) {
        return;  // 已经连接,无需再次处理
    }
    
    // 更新网络信息
    NetworkInfo info = nai.networkInfo;
    info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
    
    // 创建网络(如果还未创建)
    // 这会通知 netd 创建 Linux 网络命名空间
    createNetwork(nai);
    
    // 启动验证(如果网络要求 INTERNET 能力)
    if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) {
        startNetworkMonitor(nai);  // 开始验证网络连通性
    }
    
    // 应用现在可以开始使用这个网络
    // 触发网络重匹配,应用可能会 onAvailable() 回调
    rematchAllNetworksAndRequests();
}

markConnected() 之前的典型状态:

step 1: register() - 网络注册,初始状态 DISCONNECTED
step 2: sendNetworkCapabilities() - 上报初始能力
step 3: sendLinkProperties() - 上报初始链路属性
step 4: sendNetworkScore() - 上报初始评分
step 5: ... (多次更新能力/属性/评分)
step 6: markConnected() - ✓ 现在完全就绪

标记为已连接后会发生什么: ├─ 应用可以通过 NetworkRequest 请求该网络 ├─ 应用收到 onAvailable() 回调 ├─ 网络进入验证流程(如需要) ├─ 网络成为候选的默认网络 └─ 可以处理应用流量

【应用回调的类型与顺序】 ═════════════════════════════════════════════════════════════

假设应用注册了 NetworkCallback:

ConnectivityManager.NetworkCallback callback = 
    new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            // ← 当网络可用时调用
            // 此时网络已连接
        }
        
        @Override
        public void onCapabilitiesChanged(Network network, 
                                         NetworkCapabilities caps) {
            // ← 当网络能力改变时调用
            // 例如:获得验证、失去验证等
        }
        
        @Override
        public void onLinkPropertiesChanged(Network network, 
                                           LinkProperties lp) {
            // ← 当链路属性改变时调用
            // 例如:获得 IPv6 地址
        }
        
        @Override
        public void onLosing(Network network, int maxMsToLive) {
            // ← 网络即将丧失
            // Linger 状态,应用应准备切换
        }
        
        @Override
        public void onLost(Network network) {
            // ← 网络已完全断开
            // 不再可用
        }
    };

// 注册回调
ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
cm.registerNetworkCallback(request, callback);

典型的回调序列(WiFi 连接过程):

T=0: 用户选择 WiFi SSID 并输入密码
     └─ WiFiManager 开始连接

T=1s: WiFi 连接建立,正在获取 IP
     └─ NetworkAgent: sendLinkProperties()
     └─ App: onLinkPropertiesChanged() [仅 IP 地址]

T=2s: IP 获取完成,标记为连接
     └─ NetworkAgent: markConnected()
     └─ ConnectivityService 创建网络
     └─ App: onAvailable(network) ✓ 网络可用!

T=3s: IPv6 自动配置完成
     └─ NetworkAgent: sendNetworkCapabilities() [添加 IPv6]
     └─ App: onCapabilitiesChanged() [新增 IPv6]

T=4s: NetworkMonitor 验证互联网连接
     └─ 发送 HTTP 探测...

T=5s: 验证成功
     └─ NetworkAgent: sendNetworkCapabilities() [添加 VALIDATED]
     └─ App: onCapabilitiesChanged() [获得 VALIDATED 能力]
     └─ 网络现在是"高质量"的

T=100s: 用户走出 WiFi 范围,信号变弱
     └─ NetworkAgent: sendNetworkScore() [评分下降]
     └─ ConnectivityService 重新评估
     └─ 如果 LTE 评分更高 → 触发网络切换

T=101s: LTE 成为新的默认网络
     └─ App: onAvailable(lte_network)
     └─ App: onLosing(wifi_network, 120000) [Linger 120 秒]
     └─ WiFi 暂时保活(可能马上重连)

T=120s: Linger 期满,WiFi 断开
     └─ App: onLost(wifi_network)

现在进入第 3 部分:

### **3、NetworkFactory 工作原理**

【NetworkFactory 的三大核心职责】 ═════════════════════════════════════════════════════════════

NetworkFactory 是一个工厂类,由网络提供者(如 WifiManager、TelephonyManager) 创建和持有,用于管理该提供者能否满足特定的网络请求。

┌────────────────────────────────────────┐ │ NetworkFactory 职责 │ ├────────────────────────────────────────┤ │ 1. 评估请求 │ │ ├─ 该工厂能否满足此请求? │ │ ├─ 评分是多少? │ │ └─ 依据:能力、信号强度等 │ │ │ │ 2. 管理网络生命周期 │ │ ├─ 接收请求时启动网络 │ │ ├─ 所有请求移除时关闭网络 │ │ └─ 创建 NetworkAgent │ │ │ │ 3. 动态调整评分 │ │ ├─ 信号强度变化 → 评分变化 │ │ ├─ 重新评估所有请求 │ │ └─ 影响网络选择 │ └────────────────────────────────────────┘

【TelephonyNetworkFactory 的实现】 ═════════════════════════════════════════════════════════════

蜂窝网络工厂的核心逻辑:

public class TelephonyNetworkFactory extends NetworkFactory {
    
    // 工厂维持的所有网络请求
    private Map<NetworkRequest, Integer> mNetworkRequests = new HashMap<>();
    
    // 当前 Phone 对象(可能有多个,代表多张 SIM 卡)
    private Phone mPhone;
    
    // 多卡管理器
    private PhoneSwitcher mPhoneSwitcher;
    
    /**
     * Called by ConnectivityService when a new NetworkRequest is received
     * that matches this factory's filter.
     */
    @Override
    protected void needNetworkFor(NetworkRequest request) {
        // 步骤 1: 检查是否已有相同的请求
        if (mNetworkRequests.containsKey(request)) {
            return;  // 已有相同请求,无需重复
        }
        
        // 步骤 2: 评估是否可以满足
        // 例如:检查信号强度、是否在 Airplane Mode 等
        if (!canSatisfyRequest(request)) {
            return;  // 无法满足,拒绝
        }
        
        // 步骤 3: 如果这是第一个请求,启动数据连接
        boolean isFirstRequest = mNetworkRequests.isEmpty();
        
        // 步骤 4: 记录请求
        mNetworkRequests.put(request, TRANSPORT_CELLULAR);
        
        if (isFirstRequest) {
            // 这是第一个请求,启动数据连接
            // 例如建立 PDP Context
            startDataConnection(request);
        } else {
            // 已经有活跃的数据连接
            // 新请求可以立即使用
        }
    }
    
    /**
     * Called by ConnectivityService when a NetworkRequest is withdrawn
     */
    @Override
    protected void releaseNetworkFor(NetworkRequest request) {
        // 步骤 1: 移除请求
        mNetworkRequests.remove(request);
        
        // 步骤 2: 如果没有更多请求,关闭数据连接
        if (mNetworkRequests.isEmpty()) {
            stopDataConnection();  // 断开 PDP Context
        }
    }
    
    /**
     * 开始数据连接
     */
    private void startDataConnection(NetworkRequest request) {
        // 与 DataNetworkController 通信
        mDataNetworkController.requestDataConnection(
            request.getApnType(),   // 例如:INTERNET, IMS, MMS
            new DataConnectionCallback() {
                @Override
                public void onConnected(DataNetwork network, LinkProperties lp) {
                    // 网络已连接
                    // 创建 TelephonyNetworkAgent 向系统注册
                    TelephonyNetworkAgent agent = new TelephonyNetworkAgent(
                        mPhone,
                        mLooper,
                        network,   // DataNetwork 对象
                        score,     // 初始评分(基于信号强度)
                        config,
                        this       // provider (TelephonyNetworkFactory)
                    );
                    // agent.register() 在构造函数中调用
                }
                
                @Override
                public void onDisconnected() {
                    // 网络已断开
                    // agent 将调用 unregister()
                }
            }
        );
    }
    
    private void stopDataConnection() {
        // 通知 DataNetworkController 关闭所有 PDP Context
        mDataNetworkController.releaseDataConnection();
    }
}

关键特点: ├─ 一个 Factory 可能同时满足多个 NetworkRequest ├─ 但底层可能只有一个数据连接(PDP Context) ├─ 所有请求共享同一个 NetworkAgent ├─ 当最后一个请求移除时才断开连接

【WiFi NetworkFactory 的实现】 ═════════════════════════════════════════════════════════════

WiFi 工厂的特点:一个 WiFi 连接一个 NetworkAgent

public class WifiNetworkFactory extends NetworkFactory {
    
    // WiFi 提供的网络 Agent(通常只有一个)
    private WifiNetworkAgent mWifiAgent = null;
    
    // WiFi 管理器
    private WifiManager mWifiManager;
    
    @Override
    protected void needNetworkFor(NetworkRequest request) {
        // WiFi 工厂的逻辑相对简单
        
        // step 1: 如果已经有 WiFi 连接
        if (mWifiAgent != null && mWifiAgent.isConnected()) {
            // WiFi 网络已连接,可以满足请求
            // (无需额外操作,该网络已注册)
            return;
        }
        
        // step 2: 如果没有 WiFi,可能启动扫描
        // 或者如果之前已连接但暂时断开,自动重连
        if (mWifiManager != null) {
            // WiFi 在之前已配置,自动重连
            mWifiManager.reconnect();
        }
    }
    
    @Override
    protected void releaseNetworkFor(NetworkRequest request) {
        // WiFi 不会因为一个请求的移除就断开
        // 用户可能还在浏览网页,即使应用没有特定的网络请求
        // WiFi 应该保活
        
        // 实际上,WiFi 通常不实现这个方法
        // 或者直接 return(无操作)
    }
    
    /**
     * 当 WiFi 连接成功时,由 WifiMonitor 或 WifiManager 调用
     */
    public void onWifiConnected(String ssid, int rssi) {
        // 创建新的 WifiNetworkAgent
        mWifiAgent = new WifiNetworkAgent(
            mContext,
            mLooper,
            ssid,
            new NetworkCapabilities.Builder()
                .addTransportType(TRANSPORT_WIFI)
                .addCapability(NET_CAPABILITY_INTERNET)
                .addCapability(NET_CAPABILITY_NOT_METERED)  // 通常不计量
                .setSignalStrength(rssiToSignalLevel(rssi))
                .build(),
            linkProperties,
            networkScore,
            config,
            provider
        );
        
        mWifiAgent.register();  // 注册到系统
    }
    
    /**
     * 当 WiFi 断开连接时
     */
    public void onWifiDisconnected() {
        if (mWifiAgent != null) {
            mWifiAgent.unregister();
            mWifiAgent = null;
        }
    }
}

WiFi 和移动网络的重要区别: ├─ WiFi: 一个连接一个 Agent(多个应用共享) ├─ 移动网络: 可能有多个 PDP Context(不同 APN) └─ 关键:评分决定使用哪个网络

【网络请求的匹配算法】 ═════════════════════════════════════════════════════════════

ConnectivityService 如何决定哪个工厂来满足请求:

private void rematchAllNetworksAndRequests() {
    // 步骤 1: 获取所有网络,按评分排序
    List<NetworkAgentInfo> networks = sortByScore(mNetworkAgents);
    
    // 步骤 2: 对于每个网络,检查所有未分配的请求
    for (NetworkAgentInfo network : networks) {
        for (NetworkRequestInfo request : mNetworkRequests) {
            if (request.isAlreadyAssigned()) {
                continue;  // 已分配给其他网络
            }
            
            // 检查网络是否满足请求
            if (network.networkCapabilities.canBeSatisfiedBy(
                    request.networkRequest.getCapabilities())) {
                
                // 分配该网络给该请求
                assignNetworkToRequest(network, request);
            }
        }
    }
}

具体的匹配逻辑:

private boolean canNetworkSatisfyRequest(
    NetworkAgentInfo network, NetworkRequest request) {
    
    // 检查 1: 能力匹配
    // 请求的所有 REQUIRED 能力必须存在
    for (int capability : request.getCapabilities()) {
        if (!network.networkCapabilities.hasCapability(capability)) {
            return false;  // 网络缺少必要的能力
        }
    }
    
    // 检查 2: UNWANTED 能力
    // 请求要求的任何 UNWANTED 能力都不能存在
    for (int unwanted : request.getUnwantedCapabilities()) {
        if (network.networkCapabilities.hasCapability(unwanted)) {
            return false;  // 网络有不需要的能力
        }
    }
    
    // 检查 3: 传输类型
    // 如果请求指定了传输类型,必须匹配
    if (request.hasTransport()) {
        if (!network.networkCapabilities.hasTransport(request.getTransport())) {
            return false;  // 传输类型不匹配
        }
    }
    
    // 检查 4: 特殊的企业需求(如果有)
    // 企业 VPN, 特定运营商等
    
    return true;  // 网络可以满足请求
}

请求分配的优先级:

优先级 1: 已验证的网络 (NET_CAPABILITY_VALIDATED)
          ├─ 分配给 INTERNET 请求
          ├─ 评分越高越优先
          └─ 通常是 WiFi 或强信号的 LTE

优先级 2: 未验证但连接的网络
          ├─ 可以暂时满足请求
          ├─ 但应用可能检测到无互联网
          └─ 用于某些特殊应用(本地网络)

优先级 3: VPN 网络
          ├─ 评分高于底层网络
          ├─ 流量被 VPN 加密
          └─ 所有互联网请求被转向 VPN

优先级 4: 其他特殊网络
          ├─ 蓝牙网络
          ├─ 以太网
          └─ 本地网络(不需要互联网)

让我继续讲解第 4 部分:

### **4、网络状态管理与 Linger 机制**

【9 种关键的网络状态】 ═════════════════════════════════════════════════════════════

Android 网络栈中,一个网络可以处于以下几种状态之一:

1️⃣ IDLE / UNREGISTERED(空闲/未注册) ─────────────────────────────────────

定义: ├─ 网络还不存在或尚未被系统感知 ├─ NetworkAgent 尚未创建或已销毁 └─ 典型情况:WiFi 未连接、移动网络无信号

持续时间: ├─ 最长(直到用户连接或设备移出信号范围) └─ 无确定的终止时间

转换到: └─ → REGISTERING / CONNECTING

2️⃣ REGISTERING(注册中) ──────────────────────

定义: ├─ NetworkAgent 已创建但尚未向 ConnectivityService 注册 ├─ 或刚注册但初始状态还是 DISCONNECTED └─ 网络还不可用

示例流程:

// WiFi 或移动网络刚发现,创建 Agent
WifiNetworkAgent agent = new WifiNetworkAgent(...);
// 此时网络是 REGISTERING

// Agent 向系统注册
agent.register();
// 现在在 ConnectivityService 中记录,但状态仍是 DISCONNECTED

持续时间: ├─ 非常短(通常 < 100ms) └─ 直到 Agent 构造完成并调用 register()

转换到: └─ → CONNECTING 或 DISCONNECTED

3️⃣ CONNECTING(连接中) ────────────────────

定义: ├─ NetworkAgent 已注册,正在建立连接 ├─ 底层驱动(WiFi Driver、RIL)正在工作 ├─ 可能在身份验证、获取 IP 等阶段 └─ 网络还不能接收流量

典型的 CONNECTING 子阶段:

CONNECTING
├─ SCANNING(扫描可用网络)
├─ AUTHENTICATING(验证身份/密码)
├─ OBTAINING_IPADDR(通过 DHCP 获取 IP)
├─ VERIFYING_POOR_LINK(检查连接质量)
└─ CAPTIVE_PORTAL_CHECK(检查强制门户)

示例:

用户选择 WiFi SSID 并输入密码
↓
WifiManager.connect()
↓
WifiNetworkAgent: sendNetworkInfo(CONNECTING)
↓
ConnectivityService: 网络状态变为 CONNECTING
↓
应用看到网络在 CONNECTING(onAvailable 还未调用)
↓
经过数秒...
↓
WiFi Driver 获得 IP 地址
↓
WifiNetworkAgent: markConnected()
↓
状态转为 CONNECTED

持续时间: ├─ WiFi: 1-5 秒(通常) ├─ 移动网络: 3-10 秒(取决于信号) └─ 恶劣条件:可能超过 30 秒

转换到: ├─ → CONNECTED(连接成功) └─ → DISCONNECTED(连接失败)

4️⃣ CONNECTED(已连接) ──────────────────────

定义: ├─ 网络完全连接,L3 连通性已建立 ├─ IP 地址、网关、DNS 配置完成 ├─ 应用可以通过 NetworkRequest 使用该网络 ├─ 网络可以接收和发送数据包

这是最重要的一个状态,分为两个子阶段:

4a. CONNECTED (UNVALIDATED) ├─ 网络已连接但验证状态未知 ├─ 可能无法访问互联网(例如,需要登录门户) ├─ NetworkMonitor 可能正在验证 └─ 应用应该预期验证失败

4b. CONNECTED (VALIDATED) ├─ 网络已连接且验证通过 ├─ 有真实的互联网连接 ├─ 应用可以安全使用该网络 └─ 网络成为主要网络的候选

示例时间线:

T=0s: 网络 CONNECTED(未验证)
     └─ NetworkMonitor 启动验证

T=2-5s: 验证在进行中
      └─ 发送 HTTP 探测、DNS 查询等

T=5s: 验证完成
     ├─ 情况 A: 验证成功 → VALIDATED
     ├─ 情况 B: 检测到强制门户 → CAPTIVE_PORTAL
     └─ 情况 C: 网络离线 → UNVALIDATED

持续时间: ├─ 可以持续数秒到数小时 ├─ 直到网络断开或被替换 └─ 或者从 VALIDATED 降级到 UNVALIDATED(连接变差)

转换到: ├─ → LOSING(被更好的网络替换) ├─ → SUSPENDED(临时暂停,例如飞行模式) └─ → DISCONNECTED(网络故障或用户断开)

5️⃣ LOSING(即将丧失)⭐ Linger 状态 ───────────────────────────────────

定义: ├─ 网络即将被替换为更好的网络 ├─ 但仍保活一段时间(Linger 时间) ├─ 应用有机会平滑切换 ├─ 典型场景:WiFi 变弱,切换到 LTE,但 WiFi 还活着

何时进入 LOSING:

ConnectivityService 发现:
├─ 新网络比当前网络评分高
├─ 新网络也能满足相同的请求
├─ 设置 Linger 计时器(通常 120 秒)
└─ 告知应用:onLosing(network, maxMsToLive)

示例:

T=0s: 用户走出 WiFi 范围,WiFi 信号变弱
     └─ WiFiNetworkAgent 降低评分

T=1s: LTE 信号强于 WiFi
     └─ ConnectivityService 重新评估
     └─ 决定:LTE 是新的默认网络

T=2s: WiFi 进入 LOSING 状态
     └─ 调用所有监听者的 onLosing()
     └─ 设置 120 秒 Linger 计时器

T=2-120s: WiFi 仍可用
         ├─ 已连接的 Socket 可继续使用
         ├─ 新的 Socket 默认使用 LTE
         ├─ 应用如果愿意可以继续用 WiFi
         └─ onLosing() 告诉应用:还有 120 秒!

T=122s: Linger 时间到期
       └─ WiFi 进入 DISCONNECTED
       └─ 调用所有监听者的 onLost()

Linger 的设置:

// 默认 Linger 时间(秒)
private static final int DEFAULT_LINGER_DELAY_MS = 120 * 1000;  // 120 秒

// 不同网络类型的 Linger 时间
private static final int WIFI_LINGER_TIME_MS = 120 * 1000;      // WiFi: 120 秒
private static final int CELLULAR_LINGER_TIME_MS = 60 * 1000;   // LTE: 60 秒
private static final int VPN_LINGER_TIME_MS = 30 * 1000;        // VPN: 30 秒

Linger 的用途: ├─ 给应用时间进行平滑过渡 ├─ 避免频繁的网络切换 ├─ 保护旧网络上已建立的连接 ├─ 减少用户感知的中断

Linger 的缺点: ├─ 延迟了网络释放 ├─ 消耗电池(网络芯片保持活跃) ├─ 浪费流量(同时使用两个网络) └─ 某些 OEM 减短或关闭 Linger

6️⃣ SUSPENDED(暂停) ──────────────────

定义: ├─ 网络仍然连接,但临时不可用 ├─ 例如:飞行模式打开、设备暂停 ├─ 应用看不到该网络

触发条件:

飞行模式启用
  → PhoneStateListener.onServiceStateChanged()
  → Telephony 网络 SUSPENDED

屏幕关闭 + 某些 App 要求
  → DeviceIdleManager 发送 IDLE 信号
  → 非必要网络 SUSPENDED

VPN 连接
  → 底层网络 SUSPENDED
  → VPN 网络成为唯一的 INTERNET 网络

示例:

T=0s: WiFi 正常连接并验证
     └─ 网络: CONNECTED, VALIDATED

T=5s: 用户启用飞行模式
     └─ WifiManager.setAirplaneModeEnabled(true)
     └─ 所有网络状态: SUSPENDED
     └─ onLost() 回调(应用看不到网络)

T=10s: 用户关闭飞行模式
      └─ WiFi 恢复连接
      └─ 网络状态回到 CONNECTED
      └─ onAvailable() 回调

持续时间: ├─ 可以是秒级(临时暂停) ├─ 也可以是长时间(用户不改变设置) └─ 无自动清除,需要用户或系统事件触发恢复

转换到: ├─ → CONNECTED(恢复可用) └─ → DISCONNECTED(永久断开)

7️⃣ DISCONNECTING(断开中) ────────────────────────

定义: ├─ 网络正在优雅断开连接 ├─ 底层驱动正在进行清理(例如注销会话) ├─ 应用应该停止使用该网络 ├─ 这个状态通常很短

示例:

用户按"断开连接"按钮
↓
WifiManager.disconnect()
↓
WiFi Driver 发送断开帧到 AP
↓
NetworkAgent: sendNetworkInfo(DISCONNECTING)
↓
应用收到 onLosing() 和后续的 onLost()
↓
(数百毫秒后)
↓
Driver 确认断开完成
↓
NetworkAgent: unregister()
↓
状态变为 DISCONNECTED

持续时间: ├─ 通常 < 1 秒 ├─ WiFi: 100-500ms ├─ 移动网络: 500ms - 2 秒 └─ 若驱动有问题可能更长

转换到: └─ → DISCONNECTED(断开完成)

8️⃣ DISCONNECTED(已断开) ──────────────────────

定义: ├─ 网络已完全断开 ├─ NetworkAgent 已 unregister() ├─ 应用无法使用该网络 ├─ 这是网络生命周期的终点

可能的原因: ├─ 用户手动断开 ├─ 网络不可用(信号丧失、交换机掉线等) ├─ 设备进入飞行模式 ├─ 系统关闭 ├─ NetworkFactory 决定释放网络

示例:

WiFi 网络从 CONNECTED 到 DISCONNECTED 的完整生命周期:

T=0s: 用户连接到 WiFi
     ├─ Agent 创建: REGISTERING
     ├─ Agent 注册: CONNECTING
     ├─ 获取 IP: CONNECTING
     └─ 标记连接: CONNECTED

T=5s: 用户走出范围,信号丧失
     └─ WiFi Driver 检测到
     └─ NetworkAgent: unregister()
     └─ 状态: DISCONNECTED

T=5.1s: onLost() 回调给应用
       └─ 应用停止使用该网络

持续时间: ├─ DISCONNECTED 没有"持续时间" ├─ 它是终点,不会自动转变 └─ 需要新的网络连接事件才能回到活跃状态

从 DISCONNECTED 到: └─ → REGISTERING(新网络连接)

9️⃣ UNREGISTERED (最初始状态) ───────────────────────────

定义: ├─ 网络从未被系统知道 ├─ NetworkAgent 从未创建 ├─ 等同于 IDLE,但从系统的角度 └─ 这是网络的预初始化状态

转换到: └─ → REGISTERING(网络被发现和创建)

【状态转换的完整图表】 ═════════════════════════════════════════════════════════════

                   ┌──────────────┐
                   │ UNREGISTERED │
                   │   (初始)      │
                   └───────┬──────┘
                           │ 网络被发现
                           ▼
                   ┌──────────────┐
                   │ REGISTERING  │
                   │ (注册中)      │
                   └───────┬──────┘
                           │ register()
                           ▼
                   ┌──────────────┐
                   │ CONNECTING   │  ← WiFi 扫描、身份验证、DHCP
                   │ (连接中)      │     通常持续 1-10 秒
                   └──────┬───────┘
                          │
                   ┌──────┴───────┐
                   │              │ 连接失败或超时
                   │ markConnected()
                   │              │
                   ▼              ▼
            ┌──────────────┐  ┌──────────────┐
            │ CONNECTED    │  │ DISCONNECTED │
            │ (已连接)      │  │ (已断开)      │
            └──────┬───────┘  └──────────────┘
                   │
           ┌───────┴───────┐
           │               │
    ┌──────▼────────┐     │ 被替换
    │ VALIDATED     │     │ (Linger)
    │ (已验证)       │     │
    └──────┬────────┘     ▼
           │         ┌──────────────┐
           │         │ LOSING       │
           │         │ (即将丧失)    │  <- Linger 120 秒
           │         └────┬─────────┘
           │              │ Linger 期满
           │              ▼
           │         ┌──────────────┐
           └────────►│ DISCONNECTED │
                     │ (已断开)      │
                     └──────────────┘
           
           其他可能:
           任何状态 ──飞行模式──► SUSPENDED
           SUSPENDED ────────► 恢复网络所有状态

【Linger 机制的详细工作流程】 ═════════════════════════════════════════════════════════════

Linger 是 Android 网络栈中最重要的平滑过渡机制。

Step 1: 触发条件 - 更好的网络出现
─────────────────────────────────

当前状态:
├─ 默认网络: WiFi (评分: 75, 已验证)
├─ 待机网络: LTE (评分: 50, 已验证)
└─ 活跃应用: 浏览器,通过 WiFi 下载文件

网络评分变化:
├─ WiFi 信号变弱: -75dbm → -85dbm
├─ WiFi 评分下降: 7545
├─ LTE 评分: 50 (保持不变)
└─ 新的评分: LTE (50) > WiFi (45)

ConnectivityService 的决策:
├─ 调用 rematchAllNetworksAndRequests()
├─ 发现 LTE 现在应该是默认网络
├─ 但 WiFi 仍然满足所有活跃请求
└─ 决定:进入 LOSING (Linger)


Step 2: 进入 LOSING 状态
─────────────────────

ConnectivityService 的操作:
```java
private void handleNetworkLosing(NetworkAgentInfo nai) {
    if (nai.isLosing()) {
        return;  // 已经在 LOSING 状态
    }
    
    // 标记网络为 LOSING
    nai.setLosing();
    
    // 设置 Linger 计时器
    int lingerMs = getLingerTimeoutMs(nai.networkType);
    // lingerMs = 120000ms (默认 120 秒)
    
    mHandler.sendMessageDelayed(
        mHandler.obtainMessage(EVENT_LINGER_TIMEOUT, nai),
        lingerMs
    );
    
    // 通知所有监听者:网络即将丧失
    notifyNetworkCallbacks(nai, 
        ConnectivityManager.CALLBACK_LOSING,
        lingerMs);  // 告诉应用还有多少时间
}

应用收到的回调:

callback.onLosing(wifiNetwork, 120000);  // 还有 120 秒!

应用的典型反应:

@Override
public void onLosing(Network network, int maxMsToLive) {
    // 应用知道 WiFi 即将不可用
    // 有两种选择:
    
    // 选项 1: 立即停止使用 WiFi
    // 主动切换到 LTE
    // 优点:不会中断
    // 缺点:可能不会有 WiFi 的重新连接机会
    
    // 选项 2: 继续使用 WiFi
    // 希望 Linger 时间内 WiFi 信号恢复
    // 优点:如果信号恢复就能继续用 WiFi
    // 缺点:如果不恢复会被强制切换,导致中断
}

Step 3: Linger 期间 - 并行网络 ─────────────────────────

Linger 时间内发生的事情:

时间: 0 - 120 秒

应用的网络使用:
├─ 已建立的 Socket/连接: 继续用 WiFi(不中断)
├─ 新的网络请求: 使用 LTE(默认网络)
└─ 显式使用 WiFi 的应用: 仍然可用

系统的操作:
├─ WiFi 评分继续变化
│  ├─ 如果信号改善: 回到 CONNECTED(取消 Linger)
│  └─ 如果信号继续变差: 保持 LOSING
├─ LTE 成为默认网络
├─ 新应用使用 LTE(自动)
└─ 旧应用仍在 WiFi 上(如果显式请求)


Step 4: WiFi 信号恢复 - Linger 取消
────────────────────────────────

在 Linger 期间,如果 WiFi 信号改善:

WiFiNetworkAgent:
├─ 检测到信号改善
├─ sendNetworkScore(newScore)
│  └─ 评分提高: 4070
└─ ConnectivityService 收到更新

ConnectivityService:
```java
private void handleNetworkScoreChanged(NetworkAgentInfo nai) {
    int newScore = nai.getNetworkScore();
    
    if (nai.isLosing()) {
        // 检查是否还应该 LOSING
        if (newScore > currentDefaultNetworkScore) {
            // 评分改善,取消 Linger
            nai.clearLosing();
            mHandler.removeMessages(EVENT_LINGER_TIMEOUT, nai);
            
            // WiFi 回到 CONNECTED
            // 并可能成为默认网络(如果评分足够高)
            rematchAllNetworksAndRequests();
            
            // 通知应用:不用担心了
            notifyNetworkCallbacks(nai, 
                CALLBACK_AVAILABLE);
        }
    }
}

应用收到回调:

// WiFi 恢复,不再 LOSING
callback.onAvailable(wifiNetwork);  // 或其他能力更新回调

Step 5: Linger 期满 - 网络断开 ───────────────────────────

如果 120 秒内 WiFi 评分没有恢复到足够高:

private void handleLingerTimeout(NetworkAgentInfo nai) {
    if (!nai.isLosing()) {
        return;  // 已经恢复了
    }
    
    // Linger 期满,断开网络
    nai.clearLosing();
    
    // 标记为 DISCONNECTED
    handleNetworkDisconnected(nai);
    
    // 最终通知应用
    notifyNetworkCallbacks(nai, CALLBACK_LOST);
}

应用最终收到:

callback.onLost(wifiNetwork);  // WiFi 彻底断开

完整的时间线:

T=0s:    WiFi 信号开始变弱
         应用: 收到 onCapabilitiesChanged (评分变化,但仍 CONNECTED)

T=5s:    WiFi 评分 < LTE 评分
         ConnectivityService: 决定切换
         应用: 收到 onLosing(wifiNetwork, 120000)

T=5-60s: 用户可能靠近 WiFi 路由器
         WiFi: sendNetworkScore(改善)
         应用: 继续使用旧连接的 WiFi

T=60s:   WiFi 信号继续变差或未改善
         ConnectivityService: 评分仍 < LTE
         应用: 仍在 Linger, 仍可用

T=125s:  Linger 期满
         ConnectivityService: handleLingerTimeout()
         WiFi: 强制 DISCONNECTED
         应用: 收到 onLost(wifiNetwork)
         已建立的 WiFi 连接: 中断 ❌

【Linger 的配置与优化】 ═════════════════════════════════════════════════════════════

系统属性控制 Linger 时间:

ro.config.ip_linger_ms=120000  # 120 秒(默认)

OEM 可以自定义:

ro.config.ip_linger_ms=60000   # 激进:60 秒(早点切换)
ro.config.ip_linger_ms=300000  # 保守:5 分钟(等待网络恢复)
ro.config.ip_linger_ms=0       # 禁用 Linger(立即切换)

Linger 的优缺点:

✓ 优点: ├─ 平滑过渡:应用无需感知网络变化 ├─ 自动恢复:如果信号改善,自动回到原网络 ├─ 灵活性:应用可选择何时切换 └─ 用户体验:减少中断和延迟

✗ 缺点: ├─ 电池消耗:多个网络同时活跃 ├─ 流量浪费:可能使用两个网络 ├─ 延迟切换:不是立即切换到最好的网络 └─ 复杂性:增加网络栈的复杂度

不同 OEM 的选择: ├─ Google Pixel: 120 秒(标准) ├─ Samsung: 60-120 秒(中等) ├─ 某些国运营商: 30 秒(激进) └─ 企业设备: 0 秒(禁用,强制最好网络)


继续讲解第 5-6 部分。

好的,现在开始讲解第 5-6 部分:

---

### **5、WiFi 与移动网络切换深度解析**

【NetworkRanker 的 3 层决策模型】 ═════════════════════════════════════════════════════════════

NetworkRanker 是决定"哪个网络最好"的核心引擎。它通过一系列过滤规则, 逐步排除不合格的网络,最终选出最佳网络。

Layer 1: 硬性条件(必须满足) ────────────────────────────

这一层过滤掉完全不符合条件的网络:

┌─────────────────────────────────────────────────────┐ │ 1. 请求完全满足 (Satisfy Request) │ │ └─ 网络必须有请求要求的所有能力 │ │ 例如:请求要求 INTERNET │ │ 网络必须有 NET_CAPABILITY_INTERNET │ │ │ │ 2. 不能是即将销毁的网络 │ │ └─ POLICY_IS_DESTROYED 的网络被排除 │ │ 这样新网络可以直接替换 │ └─────────────────────────────────────────────────────┘

代码:

// 步骤 1: 过滤出能满足请求的网络
List<NetworkAgentInfo> candidates = 
    networks.stream()
        .filter(nai -> nai.satisfies(request))
        .collect(toList());

if (candidates.isEmpty()) {
    return null;  // 没有网络满足要求
}

Layer 2: 优先级过滤(Prioritization Filters) ──────────────────────────────────────────

这一层使用一系列规则逐步缩小候选范围:

规则 1: 不败的网络(Invincible Networks)

if (any network has POLICY_IS_INVINCIBLE) {
    return that network;  // 直接返回,其他网络无论多好都不行
}

用途: ├─ OEM 特殊配置的网络 ├─ 企业 VPN(如配置为不败) └─ 某些特殊场景下的专网

规则 2: VPN 网络(VPN Policy)

if (any network is VPN (POLICY_IS_VPN)) {
    return the VPN network;  // VPN 通常优先于底层网络
}

原因: ├─ VPN 是用户明确选择的加密通道 ├─ 所有互联网流量都应该通过 VPN └─ VPN 的评分通常高于底层网络

规则 3: 用户选择的网络(User Selection Policy)

if (any network has both:
    - POLICY_EVER_USER_SELECTED (用户主动连接过)
    - POLICY_ACCEPT_UNVALIDATED (用户愿意接受未验证)
) {
    prefer that network;
}

用途: ├─ 用户喜欢用某个特定的 WiFi ├─ 即使有验证失败,也应该优先 └─ 例如:家里的 WiFi,user 明确选择

规则 4: 验证状态(Validation Policy)

if (any network is VALIDATED or POLICY_ACCEPT_UNVALIDATED) {
    exclude non-validated networks;
}

逻辑: ├─ 有效的网络(已验证互联网连接)总是优于无效网络 ├─ 或者如果有网络的用户接受未验证,就排除所有未验证网络 └─ 关键的决策点

规则 5: WiFi 与蜂窝的选择(Yield to Bad WiFi Policy)

if (some networks have POLICY_YIELD_TO_BAD_WIFI) {
    // 这个规则用来处理"坏 WiFi vs 好蜂窝"的困境
    
    if (POLICY_YIELD_TO_BAD_WIFI && there are bad WiFis) {
        prefer the bad WiFi;  // 偏好坏 WiFi
    } else {
        prefer the LTE;  // 否则用 LTE
    }
}

这是最复杂的规则!详见下文。

规则 6: 非退出网络(Non-Exiting Policy)

if (any network doesn't have POLICY_EXITING) {
    exclude networks with POLICY_EXITING;  // Linger 阶段的网络
}

用途: ├─ 优先选择不在 Linger 的网络(即将消失) ├─ 避免选择一个即将断开的网络

规则 7: 主要传输方式(Primary Transport Policy)

for each SIM slot {
    if (there's a network for primary subscription) {
        prefer it over networks for secondary subscriptions;
    }
}

用途: ├─ 多卡设备,通常有一张主卡(Primary SIM) ├─ 蜂窝网络优先使用主卡的信号 └─ 次卡的网络作为备选

规则 8: 传输优先级(Transport Priority)

// 优先级顺序:以太网 > WiFi > 蓝牙 > 蜂窝
for (transport in [ETHERNET, WIFI, BLUETOOTH, CELLULAR]) {
    if (any network has this transport) {
        prefer this transport over lower ones;
    }
}

例子:

候选网络:
├─ LTE (评分 80, 已验证)
├─ WiFi (评分 60, 已验证)
└─ 以太网 (评分 30, 已验证)

按规则 8 评估:
├─ 有以太网?→ 选择以太网(即使评分最低!)

规则 9: 稳定性倾向(Stickiness)

if (currentSatisfier is still in candidates after all rules) {
    keep it;  // "粘性":倾向于保持当前网络
}

用途: ├─ 避免频繁切换(抖动) ├─ 减少用户感知到的网络切换 └─ 如果当前网络足够好,就别换

Layer 3: 评分比较(Score Comparison) ──────────────────────────────────────

如果经过所有过滤规则后仍有多个候选,则按评分排序:

int scoreDiff = networkA.getScore() - networkB.getScore();
if (scoreDiff > 0) return networkA;  // A 评分更高
else return networkB;

评分的计算(FullScore):

class FullScore {
    // WiFi 评分(0-100,基于 RSSI)
    // RSSI -40dbm: 95 分(最强)
    // RSSI -70dbm: 60 分(中等)
    // RSSI -85dbm: 25 分(弱)
    // RSSI < -90dbm: 0 分(断线)
    
    // LTE 评分(0-100,基于信号强度)
    // RSRP -75dbm: 90 分(最强)
    // RSRP -100dbm: 60 分(中等)
    // RSRP -120dbm: 20 分(弱)
    
    // 验证奖励:+20 分
    // Linger 惩罚:-30 分
    // VPN 奖励:+50 分
}

【Yield to Bad WiFi 规则详解】⭐ 最关键的 WiFi vs LTE 决策 ═════════════════════════════════════════════════════════════

这个规则解决的问题:

用户有两个网络可用:
├─ WiFi: -85dbm,未验证或强制门户("坏" WiFi)
└─ LTE: -95dbm,已验证("好" LTE)

问题:
├─ 评分上,LTE 可能更高
├─ 但用户已连接到 WiFi(可能是预期的)
├─ 系统应该保留 WiFi(即使坏)还是立即切换到 LTE?

POLICY_YIELD_TO_BAD_WIFI 是一个标志,表示:

"如果有坏但已连接的 WiFi,优先使用它,而不是切换到更好的蜂窝网络"

何时设置:

1. WiFi 已连接但验证失败(可能是强制门户)
   ├─ 用户可能想通过门户认证
   ├─ 此时不应立即切换到 LTE
   └─ 设置 YIELD_TO_BAD_WIFI

2. WiFi 信号变弱但仍连接
   ├─ 用户可能走近路由器时 WiFi 会恢复
   ├─ 不应立即切换到 LTE
   └─ 设置 YIELD_TO_BAD_WIFI

3. WiFi 虽然未验证但能上网(例如某些特殊场景)
   ├─ 可能是代理、特殊配置等
   └─ 优先保留 WiFi

算法(isPreferredBadWiFi):

private boolean isPreferredBadWiFi(NetworkScore score, NetworkCapabilities caps) {
    // 条件 1: 必须是 WiFi
    if (!caps.hasTransport(TRANSPORT_WIFI)) return false;
    
    // 条件 2: 不能是已验证的网络(已验证就不是"坏" WiFi)
    if (score.hasPolicy(POLICY_IS_VALIDATED)) return false;
    
    // 条件 3: 不能是用户主动避免的(在 UI 中取消勾选)
    if (score.hasPolicy(POLICY_AVOIDED_WHEN_UNVALIDATED)) return false;
    
    // 条件 4: 必须曾经评估过(在连接评估阶段)
    if (!score.hasPolicy(POLICY_EVER_EVALUATED)) return false;
    
    // 条件 5: 不能是活跃强制门户
    // (但如果曾经验证过,即使现在是门户也可以)
    if (caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
        // 如果曾经验证过,说明用户之前登录过
        // 现在门户可能由于某些原因重新出现
        // 仍然应该优先使用 WiFi
        if (!score.hasPolicy(POLICY_EVER_VALIDATED)) {
            return false;  // 从未验证过的门户,不优先
        }
    }
    
    return true;  // 这是一个"优先的坏 WiFi"
}

// 在网络选择中使用
applyYieldToBadWifiPolicy(validated, unvalidated) {
    if (any network has POLICY_YIELD_TO_BAD_WIFI) {
        List<BadWiFi> badWiFis = unvalidated.stream()
            .filter(nai -> isPreferredBadWiFi(nai.getScore()))
            .collect(toList());
        
        if (!badWiFis.isEmpty()) {
            // 有坏但优先的 WiFi
            // 移除所有有 YIELD_TO_BAD_WIFI 的非 WiFi 网络
            validated.removeAll(networks_with_YIELD_TO_BAD_WIFI);
            // 保留坏 WiFi
        }
    }
}

典型场景的决策流程:

场景 A: 用户在开放 WiFi(如机场)

WiFi 状态:连接,存在强制门户,未验证
LTE 状态:连接,已验证

isPreferredBadWiFi(WiFi)?
├─ 是 WiFi? ✓ YES
├─ 已验证? ✗ NO (未验证)
├─ 被用户避免? ✗ NO
├─ 曾评估过? ✓ YES
├─ 是强制门户? ✓ YES,但曾验证? ✗ NO
└─ → return FALSE (不优先)

决策:选择 LTE(已验证)❌
问题:用户可能想登录 WiFi!

场景 B: 用户在家里 WiFi(已登录过的门户)

WiFi 状态:连接,强制门户,未验证,但曾验证
LTE 状态:连接,已验证

isPreferredBadWiFi(WiFi)?
├─ 是 WiFi? ✓ YES
├─ 已验证? ✗ NO (现在未验证)
├─ 被用户避免? ✗ NO
├─ 曾评估过? ✓ YES
├─ 是强制门户? ✓ YES,但曾验证? ✓ YES
└─ → return TRUE (优先!)

决策:选择 WiFi(坏但优先)✓
原因:用户之前登录过,现在只是门户重新出现,应该保留 WiFi

场景 C: WiFi 信号弱

WiFi 状态:RSSI -88dbm,评分 25,已验证
LTE 状态:RSSI -95dbm,评分 70,已验证

isPreferredBadWiFi(WiFi)?
├─ 是 WiFi? ✓ YES
├─ 已验证? ✓ YES
└─ → return FALSE (已验证不算"坏" WiFi)

评分比较:
└─ LTE (70) > WiFi (25)

决策:选择 LTE ✓
原因:WiFi 虽然验证过,但评分太低,LTE 更好

【网络切换的完整时间线】 ═════════════════════════════════════════════════════════════

实际网络切换的发生过程:

T=0: 用户在 WiFi 覆盖范围内
     ├─ WiFi 连接,已验证 (RSSI -40dbm, 评分 95)
     ├─ LTE 连接,已验证 (RSRP -90dbm, 评分 65)
     └─ 默认网络:WiFi (95 > 65)

T=10s: 用户走出 WiFi 覆盖范围
       └─ WiFi 信号开始衰减 -40 → -50 → -60 ...

T=15s: WiFi 信号继续衰减到 -85dbm
       ├─ WiFiNetworkAgent.onSignalStrengthChanged(-85)
       ├─ 新评分:25
       ├─ ConnectivityService.onNetworkScoreChanged()
       └─ 触发 rematchAllNetworksAndRequests()

T=16s: NetworkRanker.getBestNetwork()
       ├─ 候选:WiFi (评分 25), LTE (评分 65)
       ├─ 规则 1-7:都通过 (都已验证,不在 Linger 等)
       ├─ 传输优先级:都不是以太网/蓝牙,继续
       ├─ 评分比较:LTE (65) > WiFi (25)
       └─ 结论:LTE 是更好的网络

T=16.1s: 做出切换决定
         └─ ConnectivityService: "WiFi 评分太低,应该切换到 LTE"

T=16.2s: 进入 Linger(如果 WiFi 有活跃请求)
         ├─ WiFi 状态变为 LOSING
         ├─ 设置 120 秒 Linger 计时器
         ├─ 调用 onLosing(wifiNetwork, 120000)
         └─ LTE 成为新的默认网络

T=16.3s: 应用行为改变
         ├─ 新的网络请求 → LTE
         ├─ 现有 Socket → WiFi (仍在 Linger)
         ├─ ConnectivityManager.getActiveNetwork() → LTE
         └─ 应用可能不感知(已在 Linger)

T=60s: WiFi 信号完全丧失
       ├─ WiFi 断开
       ├─ WiFi 状态变为 DISCONNECTED
       └─ onLost() 回调

T=136s: 如果 WiFi 信号一直没有恢复,Linger 期满
        └─ WiFi 被强制断开(如果仍在 Linger)

【切换的平滑度优化】 ═════════════════════════════════════════════════════════════

为了让用户无感知地切换网络,Android 采用了多种优化:

优化 1: Linger 机制 ───────────────── ├─ 给旧网络保活时间(120 秒) ├─ 已建立的连接继续用旧网络 ├─ 新连接自动用新网络 └─ 应用无需改变代码

优化 2: 连接池复用 ───────────────── HTTP: ├─ TCP 连接池绑定到网络 ├─ 新网络,新连接池 └─ 旧连接在旧网络上继续用

Socket: ├─ 使用 Network.getSocketFactory() 创建 ├─ 绑定到特定网络 └─ 网络断开时才会中断

优化 3: DNS 缓存 ────────────── ├─ 域名解析结果被缓存 ├─ 避免频繁重新解析 └─ 跨网络使用缓存结果

优化 4: 预连接 ───────────── ├─ 识别即将需要的资源 ├─ 在新网络上预先建立连接 └─ 用户切换时立即可用

优化 5: 信号预测 ─────────────── 基于历史数据: ├─ WiFi 通常在这个位置会断开 ├─ 这个时间段 LTE 信号较弱 ├─ 根据预测主动调整网络选择 └─ 减少网络卡顿


现在进入第 6 部分:

### **6、实战:网络问题诊断与优化**

【WiFi 连接失败的排查流程】 ═════════════════════════════════════════════════════════════

问题:用户无法连接到特定的 WiFi SSID

诊断步骤:

Step 1: 检查底层 WiFi 驱动 ─────────────────────────

命令:

adb logcat | grep -i "wifi"

关键日志信息:

WifiManager: [Network event callback]
WifiManager: State change: SCANNING
WifiManager: State change: CONNECTING
WifiManager: State change: AUTHENTICATING
WifiManager: State change: OBTAINING_IPADDR
WifiManager: State change: CONNECTED
WifiManager: State change: DISCONNECTED (reason=X)

常见的断开原因代码:

reason=1    : WIFI_REASON_UNSPECIFIED (未指定)
reason=2    : WIFI_REASON_WRONG_PASSWORD (密码错误)
reason=3    : WIFI_REASON_AUTH_FAILED (认证失败)
reason=4    : WIFI_REASON_NETWORK_UNAVAILABLE (网络不可用)
reason=5    : WIFI_REASON_DEAUTH_RECEIVED (收到 Deauth 帧)
reason=6    : WIFI_REASON_DISASSOC_RECEIVED (收到 Disassoc 帧)
reason=7    : WIFI_REASON_ASSOC_FAILED (关联失败)
reason=8    : WIFI_REASON_HAND_SHAKE_TIMEOUT (握手超时)

诊断:

如果日志显示 AUTHENTICATING 停留很久后跳到 DISCONNECTED
├─ 可能原因:密码错误、路由器不支持该加密方式
└─ 解决:检查密码、检查路由器配置

如果快速显示 DISCONNECTED,reason=5
├─ AP 拒绝连接(可能 MAC 过滤、安全问题)
└─ 联系 WiFi 管理员

Step 2: 检查 IP 配置 ──────────────────

命令:

adb shell ifconfig wlan0

输出示例:

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
       inet 192.168.1.100  netmask 255.255.255.0  broadcast 192.168.1.255
       inet6 fe80::xxxx:yyyy:zzzz:wwww  prefixlen 64  scopeid 0x20<link>
       ...

诊断:

如果没有 inet 地址:
├─ DHCP 没有给 IP
├─ 可能路由器 DHCP 故障
└─ 或者 WiFi 驱动问题

如果有 inet 地址,可以继续

Step 3: 检查网络连接性 ────────────────────

命令:

adb shell ping -c 4 8.8.8.8

结果:

成功:
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=57 time=25.3 ms
64 bytes from 8.8.8.8: seq=1 ttl=57 time=24.8 ms
...

失败:
PING 8.8.8.8 (8.8.8.8): 56 data bytes
(no response)

诊断:

如果 ping 成功:
├─ IP 配置正常
├─ DNS 可能有问题(见下一步)
└─ 或者是应用问题

如果 ping 失败:
├─ 网络连接确实有问题
├─ 可能原因:
│  ├─ 路由器网络故障
│  ├─ ISP 断网
│  ├─ 防火墙阻止
│  └─ 路由器设置不当

Step 4: 检查 DNS 解析 ───────────────────

命令:

adb shell nslookup google.com
或
adb shell getprop net.dns1
adb shell getprop net.dns2

结果:

成功:
Server:         8.8.8.8
Address:        8.8.8.8#53

Name:   google.com
Address: 142.251.41.14

失败:
nslookup: can't resolve google.com

诊断:

如果 DNS 失败:
├─ 路由器 DNS 故障
├─ 改用公共 DNS (8.8.8.8, 1.1.1.1)
└─ 检查路由器 DHCP 配置

如果 DNS 成功但浏览器不工作:
├─ 可能是代理问题
├─ 或者防火墙问题
└─ 或应用权限不足

Step 5: 检查 ConnectivityService 日志 ───────────────────────────────────

命令:

adb logcat -b system | grep -i "connectivity"

关键日志:

ConnectivityService: updateNetworkInfo()
ConnectivityService: handleNetworkInfoChange()
ConnectivityService: maybeNotifyNetworkCallbacks()

// 网络状态变化
NetworkInfo: State=CONNECTING -> State=CONNECTED
NetworkInfo: DetailedState=AUTHENTICATING -> DetailedState=CONNECTED

// 能力变化
NetworkCapabilities: INTERNET, NOT_METERED, NOT_ROAMING, VALIDATED

// 评分
Network score: 75

诊断:

如果看到 CONNECTED 但没有 VALIDATED:
├─ NetworkMonitor 验证失败
├─ 可能是强制门户(需要登录)
├─ 或网络确实无互联网
└─ 查看下一步的 NetworkMonitor 日志

如果没有看到网络创建(netId):
├─ 网络没有被正确注册
├─ 可能 NetworkAgent 有问题
└─ 检查 WiFi 驱动或 WifiService

Step 6: 检查 NetworkMonitor 验证状态 ─────────────────────────────────

命令:

adb logcat | grep -i "NetworkMonitor"

关键日志:

NetworkMonitor: Probing network...
NetworkMonitor: GET http://www.google.com/generate_204
NetworkMonitor: Response code: 204
NetworkMonitor: Network validation successful

或

NetworkMonitor: GET http://...
NetworkMonitor: Captive portal detected
NetworkMonitor: Response code: 302 (redirect)

或

NetworkMonitor: Probe failed
NetworkMonitor: DNS timeout

诊断:

如果看到 "validation successful":
├─ 网络已验证,应该可以用
├─ 如果应用还是不能联网,可能是应用权限问题

如果看到 "Captive portal detected":
├─ 需要登录 WiFi
├─ 系统会弹出 portal 登录窗口
└─ 用户需要在浏览器中认证

如果看到 "Probe failed":
├─ 验证失败,网络不可用
├─ 原因:没有 IPv4, IPv6, 或 DNS 问题
└─ 检查网络配置

Step 7: 检查应用权限 ──────────────────

命令:

adb shell cmd appops get com.your.app INTERNET

结果:

Allow       -> 有权限
Deny        -> 无权限
Default     -> 跟随系统设置

如果无权限:

adb shell cmd appops set com.your.app INTERNET allow

其他需要的权限:

ACCESS_NETWORK_STATE      -> 查询网络状态
ACCESS_WIFI_STATE         -> 查询 WiFi 状态
CHANGE_NETWORK_STATE      -> 切换网络

诊断:

如果应用有 INTERNET 权限但仍不能连:
├─ 可能是网络本身的问题(见前面步骤)
├─ 或应用有 bug(发送不正确的请求)
└─ 用 tcpdump 抓包检查

Step 8: 抓包分析流量 ──────────────────

命令:

adb shell tcpdump -i wlan0 -w /sdcard/traffic.pcap
// 运行应用和重现问题
adb pull /sdcard/traffic.pcap
// 用 Wireshark 分析

关键检查点:

DHCP 阶段:
├─ 设备发送 DHCP Discovery
├─ 路由器响应 DHCP Offer
├─ 设备发送 DHCP Request
└─ 路由器响应 DHCP ACK

DNS 阶段:
├─ 设备发送 DNS Query (A 或 AAAA)
├─ DNS 服务器响应结果
└─ 检查是否有超时或错误

HTTP/HTTPS 阶段:
├─ TCP 3-way handshake
├─ HTTP GET/POST 请求
├─ 检查响应状态码
└─ 检查是否有超时

【网络切换异常的定位】 ═════════════════════════════════════════════════════════════

问题:设备频繁切换 WiFi 和 LTE(抖动)

诊断:

Step 1: 确认是否真的在切换 ─────────────────────────

命令:

adb logcat -b system | grep -E "rematch|default|network.available"

日志示例(表示在切换):

T+0.0s: [WiFi] Network score: 75
T+1.5s: [LTE] Network score: 65
T+2.0s: rematchAllNetworksAndRequests() -> 选择 WiFi
T+5.0s: [WiFi] Network score: 45 (信号变弱)
T+5.1s: rematchAllNetworksAndRequests() -> 选择 LTE
T+5.2s: WiFi entering LOSING state
T+8.0s: [WiFi] Network score: 75 (信号恢复!)
T+8.1s: rematchAllNetworksAndRequests() -> 选择 WiFi (取消 Linger)
T+10.0s: (重复上面的循环)

诊断:

如果频繁看到这种日志:
├─ WiFi 信号不稳定(信号在边界,上下波动)
├─ NetworkRanker 每次评分变化都触发重新匹配
└─ 这导致频繁切换

Step 2: 检查信号强度变化 ──────────────────────

命令:

adb logcat | grep -E "SignalStrength|RSSI|RSRP"

日志示例:

WifiManager: RSSI changed: -60dbm -> -70dbm -> -60dbm -> -70dbm ...
TelephonyManager: Signal strength: 2 bars -> 3 bars -> 2 bars -> 3 bars ...

诊断:

如果看到信号在边界频繁波动:
├─ 物理位置在信号边界
├─ 用户走近/离开 WiFi 路由器
└─ 解决:建议用户移动位置或加强 WiFi 覆盖

Step 3: 检查评分计算 ──────────────────

命令:

adb logcat | grep "NetworkScore"

日志示例:

WiFi score: 60 (RSSI: -75dbm, validated: true, exiting: false)
LTE score: 65 (RSRP: -90dbm, validated: true, exiting: false)

Decision: LTE > WiFi, switch to LTE

诊断:

如果评分都正确,但频繁切换:
├─ 可能是网络排序算法的问题
├─ 或者 NetworkRanker 的 "stickiness" 没有生效
└─ 检查当前默认网络是否在 candidates 中

Step 4: 调整参数以减少切换 ─────────────────────────

系统属性:

# 增加 Linger 时间(给网络恢复更多时间)
ro.config.ip_linger_ms=300000  (默认 120000)

# 改变评分偏好
persist.sys.wifi.prefer_bad_wifi=true  (倾向于保留 WiFi)

# 禁用自动切换(不推荐)
persist.sys.disable_network_switching=true

代码层面的解决:

在 NetworkRanker 中增加"粘性"评分奖励:
├─ 如果当前网络还在 candidates 中
├─ 给当前网络 +10 分(粘性奖励)
├─ 减少不必要的切换
└─ 只在评分差异足够大时才切换

【网络性能优化】 ═════════════════════════════════════════════════════════════

优化 1: 加速网络就绪时间 ──────────────────────

问题:从无网络到有网络要 10 秒

优化方法:

a) 并行初始化

// 不推荐:串行
step1_getIPAddress();  // 3 秒
step2_startDNS();      // 2 秒
step3_startValidation();  // 5 秒
// 总耗时:10 秒

// 推荐:并行
parallel {
    task1: getIPAddress();       // 3 秒
    task2: startDNS();           // 2 秒
    task3: startValidation();    // 5 秒 (并行)
}
// 总耗时:5 秒 (最长的任务)

b) 验证缓存

如果相同 SSID 的 WiFi1 小时内验证过:
├─ 跳过验证,直接标记为 VALIDATED
├─ 或发送探测但不等结果
└─ 节省 3-5

c) 提前启动网络服务

在 CONNECTING 状态,就可以:
├─ 启动 DNS 解析
├─ 启动 HTTP 连接池
├─ 预加载常用资源
└─ 等 CONNECTED 时立即可用

优化 2: 降低网络切换的延迟 ──────────────────────────

问题:WiFi 到 LTE 的切换需要 2-3 秒

优化方法:

a) 预连接

识别即将需要的服务器:
├─ 建立 TCP 连接(SYN 发送)
├─ 建立 TLS 握手(部分完成)
└─ 当 Linger 时用户交互时,立即可用

b) 连接复用

使用 HTTP/2 和 HTTP/3:
├─ 多路复用(避免多个 TCP 连接)
├─ 连接迁移(跨网络保活连接)
└─ 头部压缩(减少重建连接的开销)

c) 缓存和离线支持

缓存关键资源:
├─ 避免网络切换时重新获取
├─ Service Worker 提供离线支持
└─ 用户无感知切换

优化 3: 减少电池消耗 ────────────────────

问题:WiFi 和 LTE 同时活跃消耗电池

优化方法:

a) 及时关闭 Linger 中的网络

当网络质量差时:
├─ 不等 120 秒 Linger 完成
├─ 立即切换并断开旧网络
└─ 省电

b) 合并 DNS 查询

不同应用的 DNS 查询:
├─ 系统缓存,避免重复查询
├─ 减少网络芯片唤醒次数
└─ 更省电

c) 智能预测

基于应用使用模式:
├─ 预测用户何时需要网络
├─ 提前建立网络,避免频繁开关
└─ 比反复开关更省电


优化 4: 提高网络稳定性
────────────────────

a) 增强型握手

在不稳定网络中: ├─ 重试机制(TCP fast open) ├─ 断线重连(快速恢复) ├─ 请求重试(自动恢复) └─ 用户感受到的中断更短


b) 多网络绑定

关键连接使用两个网络: ├─ WiFi 和 LTE 同时用 ├─ 任一网络故障自动切换 ├─ 无中断(超级用户体验)


c) 编码优化

在差网络中: ├─ 更强的纠错码 ├─ 更频繁的校验 ├─ 自动码率调整 └─ 减少重传