引言
网络连接是现代移动设备最核心的功能之一。从打开网页、收发消息,到视频通话、在线游戏,每一个网络操作背后,都有Android网络子系统在默默工作。而ConnectivityService,正是这个庞大系统的"中枢大脑"。
在本系列的第一篇文章中,我们将深入探索:
为什么ConnectivityService如此重要?
想象这样一个场景:你正在用WiFi观看高清视频,突然WiFi信号变弱。Android系统需要在1秒内完成以下复杂操作:
- 监测WiFi质量下降 - NetworkMonitor持续验证网络
- 评估切换时机 - NetworkRanker对比WiFi和移动网络得分
- 准备移动网络 - TelephonyNetworkFactory激活移动数据
- 无缝切换 - 在不中断视频的情况下迁移连接
- 通知应用 - 通过NetworkCallback告知网络变更
这一切,都由ConnectivityService协调完成。它就像交通指挥中心,管理着设备上所有网络的"交通"。
本文内容概览
-
ConnectivityService架构总览
- 核心职责与设计理念
- 与其他系统服务的关系
- Android 15的架构演进
-
NetworkAgent机制深度解析
- NetworkAgent生命周期
- 网络状态上报流程
- WiFi/Cellular的Agent实现
-
NetworkFactory工作原理
- 网络请求匹配机制
- Factory评分与竞争
- 按需网络激活
-
网络状态管理
- 9种网络状态详解
- 状态机转换流程
- Linger机制
-
WiFi与移动网络切换
- 切换决策算法
- NetworkRanker评分机制
- 无缝切换实现
-
实战:网络问题诊断
- 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 整体架构
架构分层说明:
应用层(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隧道 │
│ │ │配置 │ │ │| │ │ │
└────────┘ └──────┘ └────────┘ └──────┘ └────────┘
关键交互:
- 与NetworkStack交互 - 网络验证
// ConnectivityService触发验证
nai.networkMonitor.notifyNetworkConnected(
nai.linkProperties, nai.networkCapabilities);
// NetworkStack执行HTTP探测
// 返回验证结果(VALID/PORTAL/INVALID)
- 与Netd交互 - 路由配置
// 设置默认网络
mNetd.networkSetDefault(nai.network.netId);
// 添加路由规则
mNetd.networkAddRoute(netId, ifname, destination, nexthop);
- 与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生命周期
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 < -75dBm | WiFi → 移动网络 |
| 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返回302 | Captive 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 核心要点回顾
-
ConnectivityService架构
- 系统网络管理中枢
- 协调NetworkAgent和NetworkFactory
- 管理网络生命周期
-
NetworkAgent机制
- 9种网络状态
- 状态机驱动
- 基于Messenger通信
-
NetworkFactory工作原理
- 按需激活网络
- Capability匹配
- 评分竞争机制
-
网络切换
- NetworkRanker评分算法
- Linger优雅断开
- Hysteresis防抖动
-
网络验证
- HTTP/HTTPS探测
- Captive Portal处理
- Private DNS支持
8.2 ConnectivityService演进史
| 版本 | 关键变化 | 影响 |
|---|---|---|
| Android 5.0 | 引入NetworkRequest/NetworkCallback | 应用可主动请求网络 |
| Android 7.0 | MultiNetwork API | 应用可同时使用多个网络 |
| Android 9.0 | 强制使用HTTPS | 安全性提升 |
| Android 10.0 | NetworkStack模块化 | 可独立更新 |
| Android 11.0 | eBPF网络策略 | 性能大幅提升 |
| Android 12.0 | Network Slicing | 5G网络切片支持 |
| Android 15.0 | Satellite + 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都能游刃有余地处理。
系列文章
- 上一篇:Android 15存储子系统深度解析(三):FBE加密文件系统与存储性能优化实战
- 下一篇:Android 15网络子系统深度解析(二):NetworkPolicy流量控制、VPN框架与网络优化全解析
欢迎来我中的个人主页找到更多有用的知识和有趣的产品