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

167 阅读21分钟

引言

网络连接是现代移动设备最核心的功能之一。从打开网页、收发消息,到视频通话、在线游戏,每一个网络操作背后,都有Android网络子系统在默默工作。而ConnectivityService,正是这个庞大系统的"中枢大脑"。

在本系列的第一篇文章中,我们将深入探索:

为什么ConnectivityService如此重要?

想象这样一个场景:你正在用WiFi观看高清视频,突然WiFi信号变弱。Android系统需要在1秒内完成以下复杂操作:

  1. 监测WiFi质量下降 - NetworkMonitor持续验证网络
  2. 评估切换时机 - NetworkRanker对比WiFi和移动网络得分
  3. 准备移动网络 - TelephonyNetworkFactory激活移动数据
  4. 无缝切换 - 在不中断视频的情况下迁移连接
  5. 通知应用 - 通过NetworkCallback告知网络变更

这一切,都由ConnectivityService协调完成。它就像交通指挥中心,管理着设备上所有网络的"交通"。

本文内容概览

  1. ConnectivityService架构总览

    • 核心职责与设计理念
    • 与其他系统服务的关系
    • Android 15的架构演进
  2. NetworkAgent机制深度解析

    • NetworkAgent生命周期
    • 网络状态上报流程
    • WiFi/Cellular的Agent实现
  3. NetworkFactory工作原理

    • 网络请求匹配机制
    • Factory评分与竞争
    • 按需网络激活
  4. 网络状态管理

    • 9种网络状态详解
    • 状态机转换流程
    • Linger机制
  5. WiFi与移动网络切换

    • 切换决策算法
    • NetworkRanker评分机制
    • 无缝切换实现
  6. 实战:网络问题诊断

    • WiFi连接失败排查
    • 网络切换异常定位
    • 性能优化技巧

让我们开始这段深入Android网络核心的旅程!


一、ConnectivityService架构总览

1.1 ConnectivityService的核心职责

ConnectivityService是Android网络栈的"总管家",负责:

┌────────────────────────────────────────┐
│      ConnectivityService核心职责        │
├────────────────────────────────────────┤
│ 1. 网络生命周期管理                     │
│    - 创建/销毁网络                      │
│    - 监控网络状态                       │
│    - 处理网络切换                       │
│                                        │
│ 2. 网络请求调度                         │
│    - 接收NetworkRequest                │
│    - 分发到对应NetworkFactory          │
│    - 管理请求优先级                     │
│                                        │
│ 3. 网络能力管理                         │
│    - NetworkCapabilities匹配           │
│    - 网络评分(NetworkScore)            │
│    - 默认网络选择                       │
│                                        │
│ 4. 网络验证                            │
│    - 触发NetworkMonitor验证            │
│    - 处理Captive Portal               │
│    - 管理部分连接状态                   │
│                                        │
│ 5. 应用网络权限                         │
│    - UID网络访问控制                    │
│    - 后台数据限制                       │
│    - VPN隧道管理                       │
└────────────────────────────────────────┘

1.2 整体架构

07-01-connectivityservice-architecture.png

架构分层说明

应用层(Application Layer)

// 应用使用ConnectivityManager API
ConnectivityManager cm = (ConnectivityManager)
    context.getSystemService(Context.CONNECTIVITY_SERVICE);

// 请求网络
NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .build();

cm.requestNetwork(request, new NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 网络可用
    }
});

Framework层(ConnectivityService核心)

ConnectivityService源码位置:

packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java

核心数据结构

// frameworks/.../ConnectivityService.java (精简版)
public class ConnectivityService extends IConnectivityManager.Stub {

    // 所有NetworkAgent的集合
    private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
        new HashMap<>();

    // 所有NetworkRequest的集合
    private final SparseArray<NetworkRequestInfo> mNetworkRequests =
        new SparseArray<>();

    // 所有NetworkFactory的集合
    private final ArrayList<NetworkFactoryInfo> mNetworkFactoryInfos =
        new ArrayList<>();

    // 当前默认网络
    private NetworkAgentInfo mDefaultNetwork;

    // 网络评分器
    private final NetworkRanker mNetworkRanker;

    // 权限监控
    private final PermissionMonitor mPermissionMonitor;

    // 核心方法:注册NetworkAgent
    public void registerNetworkAgent(Messenger messenger,
                                     NetworkInfo networkInfo,
                                     LinkProperties linkProperties,
                                     NetworkCapabilities networkCapabilities,
                                     int currentScore,
                                     NetworkAgentConfig networkAgentConfig,
                                     int factorySerialNumber) {

        // 1. 创建NetworkAgentInfo
        final NetworkAgentInfo nai = new NetworkAgentInfo(
            messenger, new AsyncChannel(),
            new Network(mNextNetworkId++),
            networkInfo, linkProperties,
            networkCapabilities, currentScore,
            mContext, mHandler,
            networkAgentConfig, this,
            mNetd, mDnsResolver,
            mNMS, factorySerialNumber);

        // 2. 分配NetId
        try {
            mNetd.networkCreate(nai.network.netId,
                               NativeNetworkType.PHYSICAL);
        } catch (Exception e) {
            loge("Error creating network " + nai.network.netId);
            return;
        }

        // 3. 添加到管理列表
        mNetworkAgentInfos.put(messenger, nai);

        // 4. 通知NetworkMonitor开始验证
        nai.networkMonitor.start();

        // 5. 匹配NetworkRequest
        rematchNetworkAndRequests(nai, ReapUnvalidatedNetworks.DONT_REAP);
    }

    // 核心方法:处理NetworkRequest
    @Override
    public void requestNetwork(NetworkCapabilities nc,
                               Messenger messenger,
                               int timeoutMs,
                               IBinder binder,
                               int legacyType,
                               @CallbackFlags int callbackFlags) {

        // 1. 权限检查
        enforceAccessPermission();

        // 2. 创建NetworkRequestInfo
        final NetworkRequestInfo nri = new NetworkRequestInfo(
            messenger, new NetworkRequest(nc, legacyType,
            nextNetworkRequestId(), NetworkRequest.Type.REQUEST),
            binder);

        // 3. 保存请求
        mNetworkRequests.put(nri.request.requestId, nri);

        // 4. 发送给所有NetworkFactory
        for (NetworkFactoryInfo nfi : mNetworkFactoryInfos) {
            nfi.asyncChannel.sendMessage(
                android.net.NetworkFactory.CMD_REQUEST_NETWORK,
                nri.request);
        }

        // 5. 尝试匹配现有网络
        final NetworkAgentInfo bestNetwork =
            getBestNetwork(nri.request, null);
        if (bestNetwork != null) {
            callCallbackForRequest(nri, bestNetwork,
                                  ConnectivityManager.CALLBACK_AVAILABLE);
        }
    }
}

1.3 与其他系统服务的关系

┌──────────────────────────────────────────────┐
│           ConnectivityService                │
│              (核心协调者)                      │
└─────────────┬────────────────────────────────┘
              │
    ┌─────────┼─────────┬──────────┬──────────┐
    │         │         │          │          │
    ▼         ▼         ▼          ▼          ▼
┌────────┐ ┌──────┐ ┌────────┐ ┌──────┐ ┌────────┐
│NetworkStack│Netd │ │Telephony│ WiFi │ │VpnManager
│        │ │      │ │Service │ |Service│ │        │
│验证网络  │ │路由  │ │蜂窝网络 │ |无线   │  │VPN隧道  │
│        │ │配置   │ │        │|      │  │        │
└────────┘ └──────┘ └────────┘ └──────┘ └────────┘

关键交互

  1. 与NetworkStack交互 - 网络验证
// ConnectivityService触发验证
nai.networkMonitor.notifyNetworkConnected(
    nai.linkProperties, nai.networkCapabilities);

// NetworkStack执行HTTP探测
// 返回验证结果(VALID/PORTAL/INVALID)
  1. 与Netd交互 - 路由配置
// 设置默认网络
mNetd.networkSetDefault(nai.network.netId);

// 添加路由规则
mNetd.networkAddRoute(netId, ifname, destination, nexthop);
  1. 与TelephonyService交互 - 移动网络
// 请求移动数据连接
telephonyNetworkFactory.needNetworkFor(request);

// 接收数据连接状态变化
onDataConnectionStateChanged(state, networkType);

1.4 Android 15的架构改进

改进1:模块化NetworkStack

Android 10开始,NetworkStack从system_server独立为单独进程:

Android 9及之前:
┌────────────────────────┐
│    system_server       │
│  ┌──────────────────┐  │
│  │ConnectivityService│  │
│  │  + NetworkMonitor │  │ ← 验证逻辑耦合
│  └──────────────────┘  │
└────────────────────────┘

Android 10+:
┌────────────────────────┐     ┌──────────────┐
│    system_server       │     │NetworkStack  │
│  ┌──────────────────┐  │     │  进程         │
│  │ConnectivityService│◄─────►│┌────────────┐│
│  └──────────────────┘  │ AIDL││NetworkMonitor│
└────────────────────────┘     │└────────────┘│
                              └──────────────┘
                              ↑ 可独立更新

好处

  • NetworkStack可通过APK更新,无需OTA
  • 隔离故障,NetworkStack崩溃不影响system_server
  • 更好的安全边界

改进2:eBPF网络策略

Android 15使用eBPF替代iptables:

// kernel BPF程序 - 快速数据包过滤
SEC("cgroup/sock")
int bpf_cgroup_sock_filter(struct bpf_sock *sk) {
    __u32 uid = bpf_get_current_uid_gid();

    // 查询UID是否允许访问网络
    struct uid_permission_value *value =
        bpf_map_lookup_elem(&uid_permission_map, &uid);

    if (value && value->permission == PERMISSION_DENIED)
        return 0;  // 拒绝连接

    return 1;  // 允许连接
}

性能提升

  • iptables规则匹配:O(n)
  • eBPF哈希查找:O(1)
  • 延迟降低:~100μs → ~10μs

改进3:Multi-Network API增强

// Android 15新增:网络切片(Network Slicing)支持
NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NET_CAPABILITY_ENTERPRISE)  // 企业专网
    .setEnterpriseId(NET_ENTERPRISE_ID_1)      // 切片ID
    .build();

// 5G网络切片可以为不同业务提供差异化QoS

二、NetworkAgent机制深度解析

2.1 NetworkAgent是什么?

NetworkAgent是网络提供者(如WiFi、Cellular、Ethernet、VPN)与ConnectivityService之间的"信使"。

WiFi底层驱动
    ↓
WiFiNetworkAgent (继承NetworkAgent)
    ↓ Messenger通信
ConnectivityService

2.2 NetworkAgent生命周期

07-02-network-state-machine.png

9种核心状态详解

1. DISCONNECTED(断开)

  • 初始状态,NetworkAgent尚未创建
  • 或网络已完全销毁

2. CONNECTING(连接中)

// WiFiNetworkAgent创建时
public class WiFiNetworkAgent extends NetworkAgent {
    public WiFiNetworkAgent(Context context, ...) {
        super(context, looper, TAG,
              networkInfo,        // 初始状态:CONNECTING
              networkCapabilities,
              linkProperties,
              networkScore,
              config);
    }
}

3. CONNECTED(已连接)

// 物理连接建立后,Agent上报
sendLinkProperties(linkProperties);
sendNetworkCapabilities(networkCapabilities);
sendNetworkScore(score);

// ConnectivityService收到后更新状态 → CONNECTED

4. VALIDATING(验证中)

ConnectivityService自动触发NetworkMonitor:

// packages/modules/NetworkStack/.../NetworkMonitor.java
public void notifyNetworkConnected(LinkProperties lp,
                                   NetworkCapabilities nc) {
    // 1. HTTP探测
    ValidationProbeEvent probeResult =
        isCaptivePortal(mCleartextDnsNetwork);

    // 2. HTTPS探测
    probeResult = httpsProbe();

    // 3. DNS探测
    probeResult = sendDnsProbe(mPrivateDnsProviderHostname);

    // 4. 返回验证结果
    if (allProbesSucceeded) {
        return new ValidationResult(
            NetworkValidationResult.VALID, ...);
    }
}

验证URL(可配置)

# HTTP
http://connectivitycheck.gstatic.com/generate_204

# HTTPS
https://www.google.com/generate_204

# 返回HTTP 204表示验证通过

5. VALIDATED(已验证)

验证通过后,网络成为"候选默认网络":

// ConnectivityService.java
private void updateNetworkInfo(NetworkAgentInfo nai, NetworkInfo info) {
    final int oldScore = nai.getCurrentScore();

    if (info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
        // 网络验证通过,提升评分
        nai.setScore(oldScore + 10);

        // 重新评估默认网络
        rematchAllNetworksAndRequests();
    }
}

6. CAPTIVE_PORTAL(强制门户)

检测到需要网页登录(如公共WiFi):

// NetworkMonitor检测到HTTP 302重定向
if (probeResult.isPortal()) {
    // 显示登录通知
    showSignInNotification(nai);

    // 等待用户登录后重新验证
    scheduleReevaluation(REEVALUATION_DELAY_MS);
}

用户体验:

1. 连接WiFi
2. 系统检测到强制门户
3. 弹出通知:"需要登录WiFi网络"
4. 点击通知打开登录页面
5. 登录成功后自动重新验证
6. 状态变为VALIDATED

7. LOSING(即将丢失)

Linger机制 - 给应用迁移时间:

// packages/modules/Connectivity/.../ConnectivityService.java

// 当更好的网络出现时
private void handleLingerComplete(NetworkAgentInfo nai) {
    // Linger倒计时开始(默认30秒)
    nai.lingerExpiryMs = SystemClock.elapsedRealtime()
                        + mLingerDelayMs;

    // 通知应用网络即将失去
    for (NetworkRequestInfo nri : nai.networkRequests) {
        callCallbackForRequest(nri, nai,
            ConnectivityManager.CALLBACK_LOSING,
            mLingerDelayMs);
    }
}

应用可以:

// 应用收到CALLBACK_LOSING
@Override
public void onLosing(Network network, int maxMsToLive) {
    // 还有maxMsToLive毫秒,快速迁移连接
    if (maxMsToLive > 0 && maxMsToLive < 30000) {
        // 将TCP连接迁移到新网络
        migrateConnectionsToNewNetwork();
    }
}

8. SUSPENDED(暂停)

网络物理可用但逻辑暂停(如飞行模式):

// 进入飞行模式
when (Intent.ACTION_AIRPLANE_MODE_CHANGED) {
    if (isAirplaneModeOn) {
        // 暂停所有网络(除了local-only)
        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
            if (!nai.isLocalNetwork()) {
                nai.networkInfo.setDetailedState(
                    NetworkInfo.DetailedState.SUSPENDED,
                    null, null);
            }
        }
    }
}

9. DISCONNECTING(断开中)

主动或被动断开:

// 主动断开
public void disconnect() {
    // Agent通知ConnectivityService
    mAsyncChannel.sendMessage(CMD_NETWORK_DISCONNECT);
}

// ConnectivityService处理
case CMD_NETWORK_DISCONNECT:
    // 1. 移除路由
    mNetd.networkDestroy(nai.network.netId);

    // 2. 通知应用
    callCallbackForRequest(nri, nai, CALLBACK_LOST);

    // 3. 清理Agent
    mNetworkAgentInfos.remove(nai.messenger);
    break;

2.3 NetworkAgent实现示例:WiFiNetworkAgent

源码位置:

packages/modules/Wifi/service/java/com/android/server/wifi/WifiNetworkAgent.java

创建过程

// frameworks/.../WifiNetworkAgent.java (简化版)
public class WifiNetworkAgent extends NetworkAgent {

    private final WifiInfo mWifiInfo;

    public WifiNetworkAgent(Context context, Looper looper,
                           WifiInfo wifiInfo,
                           NetworkCapabilities nc,
                           LinkProperties lp,
                           NetworkScore score) {
        super(context, looper, "WifiNetworkAgent",
              nc, lp, score, new NetworkAgentConfig.Builder().build(),
              NetworkProvider.ID_WIFI);

        mWifiInfo = wifiInfo;
        register();  // 向ConnectivityService注册
    }

    // 当WiFi连接信息变化时
    public void sendNetworkCapabilities(NetworkCapabilities nc) {
        // 更新WiFi特定能力
        nc.setSignalStrength(mWifiInfo.getRssi());
        nc.setLinkUpstreamBandwidthKbps(
            mWifiInfo.getLinkSpeed() * 1000);

        super.sendNetworkCapabilities(nc);
    }

    // 当IP配置完成时
    public void sendLinkProperties(LinkProperties lp) {
        // WiFi接口名
        lp.setInterfaceName(mWifiInfo.getInterfaceName());

        // IP地址
        lp.addLinkAddress(new LinkAddress(ipAddress, prefixLength));

        // DNS服务器
        for (InetAddress dns : dnsServers) {
            lp.addDnsServer(dns);
        }

        // 路由
        lp.addRoute(new RouteInfo(destination, gateway, ifname));

        super.sendLinkProperties(lp);
    }
}

状态上报流程

WiFi驱动: DHCP成功获取IP
    ↓
WifiStateMachine: 解析IP配置
    ↓
WifiNetworkAgent.sendLinkProperties(lp)
    ↓
NetworkAgent.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, lp)
    ↓
ConnectivityService收到消息
    ↓
updateLinkProperties(nai, lp)
    ↓
触发NetworkMonitor验证
    ↓
notifyNetworkCallbacks(CALLBACK_IP_CHANGED)
    ↓
应用收到onLinkPropertiesChanged()回调

三、NetworkFactory工作原理

3.1 NetworkFactory的角色

NetworkFactory是"网络工厂",负责按需创建网络连接。

应用请求:"我需要WiFi网络"ConnectivityService: "哪个Factory能提供WiFi?"WiFiNetworkFactory: "我可以!"
    ↓
WiFiNetworkFactory激活WiFi连接
    ↓
创建WifiNetworkAgent并注册

3.2 Factory注册与评分

// packages/modules/Wifi/.../WifiNetworkFactory.java
public class WifiNetworkFactory extends NetworkFactory {

    public WifiNetworkFactory(Looper looper, Context context, ...) {
        super(looper, context, "WifiNetworkFactory",
              new NetworkCapabilities.Builder()
                  .addTransportType(TRANSPORT_WIFI)
                  .addCapability(NET_CAPABILITY_INTERNET)
                  .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
                  .build());

        // 注册到ConnectivityService
        register();
    }

    @Override
    protected void needNetworkFor(NetworkRequest request) {
        // 收到ConnectivityService的网络请求

        // 1. 检查是否能满足请求
        if (!request.hasCapability(NET_CAPABILITY_INTERNET)) {
            return;  // 不处理
        }

        // 2. 启动WiFi扫描
        mWifiManager.startScan();

        // 3. 连接最佳AP
        WifiConfiguration config = selectBestNetwork(scanResults);
        mWifiManager.connect(config, null);
    }

    @Override
    protected void releaseNetworkFor(NetworkRequest request) {
        // NetworkRequest被取消,可以断开WiFi
        if (noMoreRequests()) {
            mWifiManager.disconnect();
        }
    }
}

3.3 NetworkRequest匹配机制

NetworkCapabilities匹配规则

// NetworkCapabilities.java
public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
    // 1. Transport类型必须匹配
    if ((mTransportTypes & nc.mTransportTypes) != mTransportTypes) {
        return false;
    }

    // 2. 必须的Capability都要有
    if ((mNetworkCapabilities & nc.mNetworkCapabilities)
        != mNetworkCapabilities) {
        return false;
    }

    // 3. 禁止的Capability不能有
    if ((mUnwantedNetworkCapabilities & nc.mNetworkCapabilities) != 0) {
        return false;
    }

    // 4. 链路上下行带宽满足要求
    if (getLinkDownstreamBandwidthKbps() >
        nc.getLinkDownstreamBandwidthKbps()) {
        return false;
    }

    return true;
}

示例:复杂NetworkRequest

// 应用请求:高速、非计费、WiFi直连网络
NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NET_CAPABILITY_INTERNET)
    .addCapability(NET_CAPABILITY_NOT_METERED)  // 非计费
    .addTransportType(TRANSPORT_WIFI)           // 必须WiFi
    .setLinkDownstreamBandwidthKbps(10000)      // 至少10Mbps
    .setNetworkSpecifier(new WifiNetworkSpecifier.Builder()
        .setSsid("MyNetwork")                   // 指定SSID
        .build())
    .build();

cm.requestNetwork(request, callback);

ConnectivityService匹配过程

// ConnectivityService.java
private void sendUpdatedScoreToFactories(NetworkRequest request) {
    // 遍历所有Factory
    for (NetworkFactoryInfo nfi : mNetworkFactoryInfos) {

        // 计算该Factory对这个Request的匹配得分
        int score = nfi.networkCapabilities
            .calculateScore(request.networkCapabilities);

        // 发送REQUEST_NETWORK消息,附带分数
        nfi.asyncChannel.sendMessage(CMD_REQUEST_NETWORK,
                                     score, 0, request);
    }
}

3.4 Factory竞争机制

当多个Factory都能满足请求时,通过评分竞争:

// NetworkFactory.java
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case CMD_REQUEST_NETWORK:
            NetworkRequest request = (NetworkRequest) msg.obj;
            int score = msg.arg1;

            // 如果分数为0,说明不匹配,释放网络
            if (score == 0) {
                releaseNetworkFor(request);
            } else {
                // 分数越高,越应该积极响应
                if (score > mCurrentScore) {
                    needNetworkFor(request);
                }
            }
            break;
    }
}

实际场景

用户请求:联网但不指定类型

WiFiNetworkFactory收到:
  - score = 60 (WiFi已连接)
  - 无需操作,WiFi已激活

TelephonyNetworkFactory收到:
  - score = 50 (移动数据可用但未连接)
  - score < WiFi,不激活移动数据

VpnNetworkFactory收到:
  - score = 0 (VPN无法满足普通请求)
  - 释放VPN请求(如果有)

最终:使用WiFi网络

四、WiFi与移动网络切换

4.1 切换触发条件

// ConnectivityService.java
private void rematchAllNetworksAndRequests() {
    // 1. 重新评估所有网络
    for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
        updateNetworkScore(nai);
    }

    // 2. 重新选择最佳网络
    NetworkAgentInfo newDefault = getBestNetwork();

    // 3. 如果默认网络变化
    if (newDefault != mDefaultNetwork) {
        // 触发切换
        makeDefault(newDefault);
    }
}

常见触发场景

场景触发条件切换方向
WiFi信号变弱RSSI < -75dBmWiFi → 移动网络
WiFi验证失败HTTP探测失败WiFi → 移动网络
移动数据打开用户手动启用无 → 移动网络
连接到更好的WiFi新WiFi RSSI更高移动网络 → WiFi
移出WiFi覆盖WiFi断开WiFi → 移动网络

4.2 NetworkRanker评分算法

// packages/modules/Connectivity/.../NetworkRanker.java
public class NetworkRanker {

    public int rankNetwork(NetworkAgentInfo nai) {
        int score = 0;

        // 1. 基础分数(Transport类型)
        if (nai.networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
            score += 60;  // WiFi基础分60
        } else if (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
            score += 50;  // 移动网络基础分50
        } else if (nai.networkCapabilities.hasTransport(TRANSPORT_ETHERNET)) {
            score += 70;  // 以太网基础分70
        }

        // 2. 验证状态加分
        if (nai.everValidated) {
            score += 10;  // 曾经验证通过+10分
        }
        if (nai.lastValidated) {
            score += 20;  // 最近验证通过+20分
        }

        // 3. 网络能力加分
        if (nai.networkCapabilities.hasCapability(
            NET_CAPABILITY_NOT_METERED)) {
            score += 5;  // 非计费网络+5分
        }
        if (nai.networkCapabilities.hasCapability(
            NET_CAPABILITY_NOT_ROAMING)) {
            score += 5;  // 非漫游+5分
        }

        // 4. 信号强度影响(WiFi)
        if (nai.networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
            int rssi = nai.networkCapabilities.getSignalStrength();
            if (rssi > -60) score += 10;      // 优秀信号
            else if (rssi > -70) score += 5;  // 良好信号
            else if (rssi < -80) score -= 10; // 差信号扣分
        }

        // 5. 链路速度影响
        int linkSpeed = nai.networkCapabilities
            .getLinkDownstreamBandwidthKbps();
        if (linkSpeed > 100000) score += 10;  // >100Mbps
        else if (linkSpeed > 10000) score += 5; // >10Mbps

        // 6. VPN特殊处理
        if (nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
            score += 101;  // VPN总是优先
        }

        return score;
    }
}

示例计算

场景:WiFi和LTE同时可用

WiFi网络:
  基础分: 60
  验证通过: +20
  非计费: +5
  信号强度(-65dBm): +5
  速度(50Mbps): +5
  总分: 95

LTE网络:
  基础分: 50
  验证通过: +20
  漫游: 0
  总分: 70

结果:WiFi得分更高,成为默认网络

4.3 无缝切换实现

第一阶段:准备新网络

// ConnectivityService.java
private void makeDefault(NetworkAgentInfo newDefault) {
    NetworkAgentInfo oldDefault = mDefaultNetwork;

    if (oldDefault == null) {
        // 首次建立默认网络
        setDefaultNetwork(newDefault);
        return;
    }

    // 1. 配置新网络路由(但不设为默认)
    mNetd.networkAddRoute(newDefault.network.netId,
                         newDefault.linkProperties.getInterfaceName(),
                         "0.0.0.0/0", null);

    // 2. 通知应用新网络可用
    callCallbackForRequest(nri, newDefault, CALLBACK_AVAILABLE);

    // 3. 给旧网络30秒Linger时间
    handleLingerComplete(oldDefault);
}

第二阶段:迁移连接

// 应用收到onAvailable回调
@Override
public void onAvailable(Network network) {
    // 主动将Socket绑定到新网络
    network.bindSocket(mySocket);

    // 或使用Network.openConnection
    HttpURLConnection conn = (HttpURLConnection)
        network.openConnection(new URL("https://example.com"));
}

第三阶段:切换默认网络

// Linger超时后,切换默认路由
private void handleLingered(NetworkAgentInfo nai) {
    // 1. 移除旧网络默认路由
    mNetd.networkClearDefault();

    // 2. 设置新网络为默认
    mNetd.networkSetDefault(mDefaultNetwork.network.netId);

    // 3. 通知应用旧网络已丢失
    callCallbackForRequest(nri, nai, CALLBACK_LOST);

    // 4. 销毁旧NetworkAgent
    nai.networkMonitor.stop();
    mNetworkAgentInfos.remove(nai.messenger);
}

用户体验

时刻0s: WiFi信号变弱
  → NetworkMonitor检测到验证失败
  → NetworkRanker重新评分
  → LTE得分超过WiFi

时刻0.1s: 准备LTE网络
  → TelephonyNetworkFactory激活数据连接
  → 3-5秒后LTE连接建立

时刻5s: 通知应用
  → 发送CALLBACK_AVAILABLE(LTE)
  → 应用开始迁移连接

时刻5-35s: Linger期
  → WiFi和LTE并存
  → 新连接使用LTE
  → 旧连接仍用WiFi

时刻35s: 完成切换
  → LTE成为默认网络
  → 发送CALLBACK_LOST(WiFi)
  → WiFi断开

用户感知:几乎无感知切换

4.4 避免频繁切换

问题:WiFi信号在临界值附近波动导致频繁切换

解决方案:Hysteresis(滞后)机制

// NetworkRanker.java
private boolean shouldSwitchToNewNetwork(NetworkAgentInfo oldNai,
                                        NetworkAgentInfo newNai) {
    int oldScore = rankNetwork(oldNai);
    int newScore = rankNetwork(newNai);

    // 新网络必须显著优于旧网络才切换
    final int HYSTERESIS = 10;  // 滞后阈值

    if (newScore > oldScore + HYSTERESIS) {
        return true;  // 新网络显著更好
    }

    // 避免抖动:仅当新网络远优于旧网络时才切换
    return false;
}

效果

不使用滞后:
时间  WiFi RSSI  分数  默认网络
0s    -71dBm    65    WiFi
1s    -73dBm    63    WiFi
2s    -75dBm    60    → 切换到LTE
3s    -73dBm    63    → 切换到WiFi
4s    -75dBm    60    → 切换到LTE  (频繁抖动)

使用滞后(阈值10):
0s    -71dBm    65    WiFi
1s    -73dBm    63    WiFi
2s    -75dBm    60    WiFi  (60 vs 50+10=60, 不切换)
3s    -77dBm    55    → 切换到LTE (55 < 50+10)
4s    -73dBm    63    LTE   (63 < 50+10, 仍用LTE)

五、网络验证与Captive Portal处理

5.1 NetworkMonitor验证流程

源码位置:

packages/modules/NetworkStack/src/android/net/captiveportal/CaptivePortalProbeResult.java
packages/modules/NetworkStack/src/android/net/NetworkMonitor.java

完整验证流程

// NetworkMonitor.java (简化版)
public class NetworkMonitor extends StateMachine {

    private static final String DEFAULT_HTTP_URL =
        "http://connectivitycheck.gstatic.com/generate_204";
    private static final String DEFAULT_HTTPS_URL =
        "https://www.google.com/generate_204";

    private CaptivePortalProbeResult isCaptivePortal() {
        // 1. HTTP探测
        CaptivePortalProbeResult result = sendHttpProbe(
            mCleartextDnsNetwork, VALIDATION_TYPE_HTTP);

        if (result.isSuccessful()) {
            // HTTP 204返回,说明可以访问互联网
            return result;
        }

        if (result.isPortal()) {
            // HTTP重定向,说明是Captive Portal
            return result;
        }

        // 2. HTTPS探测(更可靠)
        result = sendHttpProbe(
            mCleartextDnsNetwork, VALIDATION_TYPE_HTTPS);

        if (result.isSuccessful()) {
            return result;
        }

        // 3. DNS探测(检查DNS是否被劫持)
        result = sendDnsProbe(mPrivateDnsProviderHostname);

        return result;
    }

    private CaptivePortalProbeResult sendHttpProbe(
            Network network, int validationType) {

        URL url = (validationType == VALIDATION_TYPE_HTTPS)
                ? mHttpsUrl : mHttpUrl;

        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) network.openConnection(url);
            conn.setInstanceFollowRedirects(false);
            conn.setConnectTimeout(SOCKET_TIMEOUT_MS);
            conn.setReadTimeout(SOCKET_TIMEOUT_MS);
            conn.setUseCaches(false);
            conn.addRequestProperty("Connection", "close");

            long start = SystemClock.elapsedRealtime();
            conn.getInputStream();
            long latency = SystemClock.elapsedRealtime() - start;

            int responseCode = conn.getResponseCode();
            String location = conn.getHeaderField("Location");

            // HTTP 204 = 验证通过
            if (responseCode == 204) {
                return new CaptivePortalProbeResult(
                    CaptivePortalProbeResult.SUCCESS, latency);
            }

            // HTTP 200 = 可能是Captive Portal
            if (responseCode >= 200 && responseCode <= 399) {
                return new CaptivePortalProbeResult(
                    CaptivePortalProbeResult.PORTAL,
                    location, latency);
            }

            // 其他 = 验证失败
            return new CaptivePortalProbeResult(
                CaptivePortalProbeResult.FAILED, latency);

        } catch (IOException e) {
            return new CaptivePortalProbeResult(
                CaptivePortalProbeResult.FAILED, 0);
        } finally {
            if (conn != null) conn.disconnect();
        }
    }
}

5.2 Captive Portal登录流程

场景:连接星巴克WiFi

Step 1: 物理连接
  用户: 选择"Starbucks WiFi"
  WiFi: DHCP获取IP (192.168.1.100)
  WiFiNetworkAgent: 上报CONNECTED状态

Step 2: NetworkMonitor验证
  HTTP探测  http://connectivitycheck.gstatic.com/generate_204
  服务器返回: HTTP 302 Redirect
  Location: http://星巴克登录页面.com/login

  结论: 检测到Captive Portal

Step 3: 通知用户
  系统通知: "需要登录WiFi网络"
  点击后打开: CaptivePortalLoginActivity

Step 4: 用户登录
  WebView加载登录页
  用户输入信息 / 同意条款
  登录成功

Step 5: 重新验证
  NetworkMonitor: 再次发送HTTP探测
  服务器返回: HTTP 204 (验证通过)
  状态: CAPTIVE_PORTAL  VALIDATED

代码实现

// CaptivePortalLoginActivity.java
public class CaptivePortalLoginActivity extends Activity {

    private WebView mWebView;
    private Network mNetwork;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 获取要验证的网络
        mNetwork = getIntent().getParcelableExtra(
            ConnectivityManager.EXTRA_NETWORK);

        // 设置WebView使用该网络
        mWebView = findViewById(R.id.webview);
        mWebView.setWebViewClient(new PortalWebViewClient());

        // 加载登录页面
        String url = getIntent().getStringExtra(
            ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
        mWebView.loadUrl(url);
    }

    private class PortalWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            // 检查是否登录成功
            if (isSuccessUrl(url)) {
                // 通知系统重新验证
                notifyPortalLoginSuccess();
                finish();
                return true;
            }
            return false;
        }
    }

    private void notifyPortalLoginSuccess() {
        ConnectivityManager cm = getSystemService(
            ConnectivityManager.class);
        cm.reportNetworkConnectivity(mNetwork, true);
    }
}

5.3 Android 15 Private DNS支持

DoT (DNS over TLS) - 加密DNS查询:

// NetworkMonitor.java - Android 15新增
private CaptivePortalProbeResult sendDnsProbe(String hostname) {
    if (isPrivateDnsValidationRequired()) {
        // 使用TLS加密的DNS查询
        try {
            SSLSocket socket = (SSLSocket)
                SSLSocketFactory.getDefault()
                    .createSocket(mPrivateDnsServerIp, 853);

            socket.startHandshake();

            // 发送DNS查询
            DnsPacket query = DnsPacket.createQuery(
                hostname, TYPE_A);
            socket.getOutputStream().write(query.getBytes());

            // 接收响应
            DnsPacket response = DnsPacket.parse(
                socket.getInputStream());

            return new CaptivePortalProbeResult(
                CaptivePortalProbeResult.SUCCESS, 0);

        } catch (Exception e) {
            return new CaptivePortalProbeResult(
                CaptivePortalProbeResult.FAILED, 0);
        }
    }
}

配置Private DNS

# 设置 → 网络和互联网 → 私人DNS
adb shell settings put global private_dns_mode hostname
adb shell settings put global private_dns_specifier dns.google

# 验证
adb shell getprop net.dns1
# 输出: 8.8.8.8 (使用Google Public DNS)

六、实战:网络问题诊断

6.1 WiFi连接失败排查

症状:WiFi显示"已连接"但无法上网

诊断步骤

# Step 1: 检查网络状态
adb shell dumpsys connectivity | grep -A 30 "Active networks"

# 输出示例:
# NetworkAgentInfo{ ni{[WIFI () - 100]}
#   network{151}  handle{151}
#   lp{{InterfaceName: wlan0
#       LinkAddresses: [ 192.168.1.100/24 ]
#       DnsAddresses: [ 192.168.1.1 ]
#       Routes: [ 0.0.0.0/0 -> 192.168.1.1 wlan0 ]}}
#   nc{[ Transports: WIFI
#        Capabilities: NOT_METERED
#        NOT_ROAMING INTERNET NOT_SUSPENDED VALIDATED]}  ← 注意这里
#   Score{current=60 validated=true}}

# 关键字段:
# - VALIDATED: 网络已验证 (如果没有此字段,说明验证失败)
# - DnsAddresses: DNS服务器 (检查是否正确)
# - LinkAddresses: 本机IP (检查是否在合理范围)
# Step 2: 检查网络验证结果
adb shell dumpsys network_stack | grep -i "validation"

# 输出:
# ValidationProbeEvent{latency=234 result=204 ...}  ← HTTP 204表示通过
# 或
# ValidationProbeEvent{latency=5000 result=timeout ...}  ← 超时
# Step 3: 手动测试连通性
adb shell

# Ping网关
ping -c 4 192.168.1.1
# 如果不通,说明局域网问题

# Ping DNS
ping -c 4 8.8.8.8
# 如果不通,说明路由问题

# Ping域名
ping -c 4 www.google.com
# 如果不通,说明DNS问题

# HTTP测试
curl -I http://connectivitycheck.gstatic.com/generate_204
# 应该返回 HTTP/1.1 204 No Content

常见原因与解决方案

现象原因解决方案
Ping网关不通WiFi驱动/硬件问题重启WiFi,检查驱动日志
Ping DNS不通路由器未设置默认网关检查DHCP配置
Ping域名不通DNS服务器故障手动设置DNS为8.8.8.8
HTTP返回302Captive Portal打开浏览器完成登录
HTTP超时防火墙拦截检查iptables规则

6.2 网络切换异常定位

症状:从WiFi切换到移动网络后,应用无法联网

诊断

# 1. 查看当前默认网络
adb shell dumpsys connectivity | grep "Active default"
# 输出: Active default network: 151

# 2. 查看该网络详情
adb shell dumpsys connectivity networks 151
# 确认是否是期望的网络(WiFi/LTE)

# 3. 检查应用是否绑定了旧网络
adb shell dumpsys connectivity requests | grep "YOUR_PACKAGE"
# 输出会显示应用的NetworkRequest和绑定的Network

# 4. 抓取网络切换日志
adb logcat -b all | grep -E "ConnectivityService|NetworkAgent"

典型日志分析

# 正常切换日志:
ConnectivityService: NetworkAgentInfo [WIFI] validation passed
ConnectivityService: Setting default network to 151 (WIFI)
ConnectivityService: Lingering network 149 (MOBILE)
... (30秒后)
ConnectivityService: Tearing down network 149

# 异常切换日志:
ConnectivityService: NetworkAgentInfo [MOBILE] validation failed
ConnectivityService: Not setting default to unvalidated network
← 移动网络验证失败,未切换

解决方案

// 应用代码修复:使用ConnectivityManager.NetworkCallback
ConnectivityManager cm = ...;
NetworkCallback callback = new NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 网络可用时,将Socket绑定到新网络
        try {
            network.bindSocket(mySocket);
            // 或重新创建连接
            recreateConnection(network);
        } catch (IOException e) {
            Log.e(TAG, "Failed to bind socket", e);
        }
    }

    @Override
    public void onLost(Network network) {
        // 网络丢失时,清理资源
        closeConnection();
    }
};

// 注册监听
cm.registerDefaultNetworkCallback(callback);

6.3 性能优化:减少网络切换延迟

问题:WiFi→LTE切换需要5-8秒

优化方案

方案1:预连接移动网络

// Android 12引入的MultiPath API
ConnectivityManager cm = ...;

// 请求同时使用WiFi和LTE
NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NET_CAPABILITY_INTERNET)
    .build();

cm.requestNetwork(request, new NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 保持LTE连接处于激活状态
        // 当WiFi断开时,可立即切换
    }
}, MULTI_PATH_PREFERENCE_PERFORMANCE);

方案2:快速失败检测

// 缩短验证超时时间
adb shell settings put global captive_portal_http_url_timeout_ms 3000

// 默认值:10000ms (10秒)
// 优化后:3000ms (3秒)

方案3:应用层优化 - Happy Eyeballs

// RFC 8305: 同时尝试IPv4和IPv6,使用最快响应的
public Socket connectWithHappyEyeballs(String host, int port) {
    List<InetAddress> addresses =
        InetAddress.getAllByName(host);  // 获取所有IP

    List<Future<Socket>> futures = new ArrayList<>();
    ExecutorService executor = Executors.newCachedThreadPool();

    // 并发连接所有地址
    for (InetAddress addr : addresses) {
        futures.add(executor.submit(() -> {
            Socket socket = new Socket();
            socket.connect(new InetSocketAddress(addr, port), 1000);
            return socket;
        }));
    }

    // 返回第一个成功的连接
    for (Future<Socket> future : futures) {
        try {
            return future.get(2, TimeUnit.SECONDS);
        } catch (Exception e) {
            // 继续尝试下一个
        }
    }

    throw new IOException("All connection attempts failed");
}

七、Android 15新特性

7.1 Satellite Connectivity(卫星连接)

Android 15新增卫星网络支持:

// 检测卫星网络可用性
NetworkCapabilities nc = cm.getNetworkCapabilities(network);

if (nc.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE)) {
    // 这是卫星连接
    // 特点:高延迟(500-800ms)、低带宽(几Kbps)、高成本

    // 应用应该:
    // 1. 使用文本通信而非语音/视频
    // 2. 压缩数据
    // 3. 减少请求频率
}

7.2 Network Slicing (5G网络切片)

// 请求特定的5G切片
NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NET_CAPABILITY_ENTERPRISE)
    .setEnterpriseId(NET_ENTERPRISE_ID_1)  // 企业专网切片
    .build();

cm.requestNetwork(request, new NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 使用企业专网,享受:
        // - 更低延迟 (<10ms)
        // - 更高带宽保证
        // - 更好的QoS
    }
});

7.3 Thread Network Support

Android 15支持Thread协议(用于智能家居):

// 连接Thread网络
NetworkRequest request = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
    .build();

// Thread特点:
// - 低功耗
// - Mesh网络
// - IPv6 only
// - 用于IoT设备

八、总结与展望

8.1 核心要点回顾

  1. ConnectivityService架构

    • 系统网络管理中枢
    • 协调NetworkAgent和NetworkFactory
    • 管理网络生命周期
  2. NetworkAgent机制

    • 9种网络状态
    • 状态机驱动
    • 基于Messenger通信
  3. NetworkFactory工作原理

    • 按需激活网络
    • Capability匹配
    • 评分竞争机制
  4. 网络切换

    • NetworkRanker评分算法
    • Linger优雅断开
    • Hysteresis防抖动
  5. 网络验证

    • HTTP/HTTPS探测
    • Captive Portal处理
    • Private DNS支持

8.2 ConnectivityService演进史

版本关键变化影响
Android 5.0引入NetworkRequest/NetworkCallback应用可主动请求网络
Android 7.0MultiNetwork API应用可同时使用多个网络
Android 9.0强制使用HTTPS安全性提升
Android 10.0NetworkStack模块化可独立更新
Android 11.0eBPF网络策略性能大幅提升
Android 12.0Network Slicing5G网络切片支持
Android 15.0Satellite + Thread扩展连接场景

8.3 未来展望

趋势1:AI驱动的网络管理

# 未来可能的实现
class AINetworkRanker:
    def __init__(self):
        self.model = load_ml_model("network_predictor.tflite")

    def predict_network_quality(self, nai):
        features = extract_features(nai)  # RSSI, 速度, 延迟等
        score = self.model.predict(features)
        return score

趋势2:Seamless Multi-Path

  • 同时使用WiFi+5G,自动负载均衡
  • 关键数据走5G(低延迟),大数据走WiFi(省流量)

趋势3:边缘计算集成

  • ConnectivityService感知MEC(多接入边缘计算)节点
  • 自动将流量路由到最近的边缘服务器

九、参考资源

9.1 源码路径

# ConnectivityService核心
packages/modules/Connectivity/service/src/com/android/server/
├── ConnectivityService.java          # 核心服务
├── NetworkAgentInfo.java             # Agent信息
├── NetworkRequestInfo.java           # 请求信息
└── connectivity/
    ├── NetworkRanker.java            # 网络排名
    └── PermissionMonitor.java        # 权限监控

# NetworkStack
packages/modules/NetworkStack/src/
├── android/net/NetworkMonitor.java    # 网络验证
├── android/net/apf/                   # APF包过滤
└── android/net/ip/IpClient.java       # IP配置

# NetworkAgent实现
packages/modules/Wifi/service/java/com/android/server/wifi/
└── WifiNetworkAgent.java              # WiFi Agent

frameworks/opt/telephony/src/java/com/android/internal/telephony/
└── dataconnection/DcNetworkAgent.java # Cellular Agent

9.2 调试命令

# 查看所有网络
adb shell dumpsys connectivity networks

# 查看默认网络
adb shell dumpsys connectivity | grep "Active default"

# 查看NetworkRequest
adb shell dumpsys connectivity requests

# 查看NetworkFactory
adb shell dumpsys connectivity factories

# 触发网络验证
adb shell cmd connectivity force-network-validation <netId>

# 模拟网络切换
adb shell svc wifi disable && sleep 2 && adb shell svc wifi enable

# 查看eBPF统计
adb shell dumpsys connectivity trafficcontroller

9.3 官方文档

9.4 相关RFC

  • RFC 3093: DHCP
  • RFC 8305: Happy Eyeballs v2
  • RFC 7858: DNS over TLS
  • RFC 8910: Captive Portal API

后记

ConnectivityService是Android系统中最复杂的服务之一,管理着设备与外界的所有网络连接。从简单的WiFi连接,到复杂的多网络并发、VPN隧道、5G切片,ConnectivityService都能游刃有余地处理。

系列文章


欢迎来我中的个人主页找到更多有用的知识和有趣的产品