为什么 Modem 中也需要存储 APN?
1. 核心原因总结
Modem 中存储 APN 主要有以下关键原因:
1.1 快速响应场景
| 场景 | 为什么需要 Modem 存储 APN |
|---|---|
| Initial Attach(初始附着) | 设备开机后,Android 可能还未启动完成,但 Modem 需要立即连接到网络 |
| 飞行模式切换 | 飞行模式关闭后,Modem 需要快速重新附着到网络 |
| 网络重选/切换 | 网络切换时,Modem 需要快速建立数据连接 |
| 紧急呼叫 | 即使 Android 系统崩溃,Modem 也能建立紧急数据连接 |
2. Initial Attach(初始附着)详解
2.1 什么是 Initial Attach?
Initial Attach 是设备开机后第一次连接到网络的过程,此时:
- Android 系统可能还未完全启动
- Framework 层的 APN 数据库还未读取
- 但 Modem 必须立即连接到网络以注册到运营商
2.2 Initial Attach 的工作流程
┌──────────────────────────────────────────────────┐
│ 1. 设备开机 / 飞行模式关闭 │
└───────────────────┬──────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────┐
│ 2. Modem 启动,需要附着到网络 │
│ • Android 系统还未完全启动 │
│ • Framework 层 APN 数据库未加载 │
└───────────────────┬──────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────┐
│ 3. Modem 使用**存储在 Modem 中的 APN** │
│ • 这些 APN 是之前通过 setInitialAttachApn() │
│ 同步到 Modem 的 │
└───────────────────┬──────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────┐
│ 4. Modem 成功附着到网络 │
│ • 设备可以接收短信、来电 │
│ • 基本数据连接建立 │
└───────────────────┬──────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────┐
│ 5. Android 系统完全启动后 │
│ • Framework 重新评估 APN 配置 │
│ • 如有必要,更新 Modem 中的 APN │
└──────────────────────────────────────────────────┘
3. Persistent Data Profile(持久化数据配置)
3.1 什么是 Persistent?
// RILUtils.java
// profile id is only meaningful when it's persistent on the modem.
dpi.profileId = (dpi.persistent) ? dp.getProfileId()
: android.hardware.radio.data.DataProfileInfo.ID_INVALID;
persistent 标志:
- true:该 APN 配置会存储在 Modem 的非易失性存储器中
- false:该 APN 配置仅在当前会话有效,重启后丢失
3.2 为什么需要 Persistent?
// DataProfileManager.java
/**
* Update the data profile used for initial attach.
*/
private void updateInitialAttachDataProfileAtModem(boolean forceUpdateIa) {
DataProfile initialAttachDataProfile = null;
// 搜索支持 Initial Attach 的 APN
for (int apnType : mDataConfigManager.getAllowedInitialAttachApnTypes()) {
initialAttachDataProfile = allDataProfiles.stream()
.filter(dp -> dp.canSatisfy(DataUtils.apnTypeToNetworkCapability(apnType)))
.findFirst()
.orElse(null);
if (initialAttachDataProfile != null) break;
}
if (forceUpdateIa || !Objects.equals(mInitialAttachDataProfile, initialAttachDataProfile)) {
mInitialAttachDataProfile = initialAttachDataProfile;
logl("Initial attach data profile updated as " + mInitialAttachDataProfile);
// 发送到 Modem,Modem 会持久化存储
mWwanDataServiceManager.setInitialAttachApn(mInitialAttachDataProfile,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
}
关键点:
- Android 选择合适的 Initial Attach APN
- 通过
setInitialAttachApn()发送到 Modem - Modem 持久化存储该 APN
- 下次开机时,Modem 直接使用存储的 APN
4. Profile ID 的作用
4.1 Profile ID 概念
// RILUtils.java (line 956-961)
dpi.persistent = dp.isPersistent();
dpi.preferred = dp.isPreferred();
// profile id is only meaningful when it's persistent on the modem.
dpi.profileId = (dpi.persistent) ? dp.getProfileId()
: android.hardware.radio.V1_0.DataProfileId.INVALID;
Profile ID:
- 是 Modem 中 APN 配置的唯一标识符
- 只有
persistent = true的 Data Profile 才有有效的 Profile ID - 用于在 Modem 中快速查找和引用 APN 配置
4.2 Profile ID 的使用场景
// DataProfileManager.java
/**
* Update the data profiles at modem.
*/
private void updateDataProfilesAtModem() {
log("updateDataProfilesAtModem: set " + mAllDataProfiles.size() + " data profiles.");
// 将所有 Data Profile 发送到 Modem
mWwanDataServiceManager.setDataProfile(mAllDataProfiles,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
Modem 会:
- 接收所有 Data Profile
- 为
persistent = true的 Profile 分配 Profile ID - 存储到 Modem 的非易失性存储器
- 在需要时通过 Profile ID 快速查找
5. 两层存储架构的对比
5.1 Android Framework 层存储
// DataProfileManager.java
// APN 数据库查询
Cursor cursor = mPhone.getContext().getContentResolver().query(
Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, "filtered/subId/"
+ mPhone.getSubId()), null, null, null, Telephony.Carriers._ID);
存储位置:
- 数据库:
/data/user_de/0/com.android.providers.telephony/databases/telephony.db - 表名:
carriers表 - 特点:
- ✅ 灵活,易于修改
- ✅ 支持复杂查询和过滤
- ❌ 依赖 Android 系统启动
- ❌ 系统崩溃后无法访问
5.2 Modem 层存储
存储位置:
- Modem 的非易失性存储器(NVRAM/EFS)
- 由 Modem Firmware 直接管理
特点:
- ✅ 独立于 Android 系统
- ✅ 开机即可用,无需等待系统启动
- ✅ 紧急场景可用(如系统崩溃)
- ❌ 存储容量有限(通常只存储几个关键 APN)
- ❌ 修改需要通过 RIL 接口
6. 实际应用场景
6.1 场景 1:设备首次开机
时间线:
T0: 用户按下电源键
T1: Modem 固件启动(Android 未启动)
T2: Modem 需要附着到网络
→ 使用 Modem 中存储的 Initial Attach APN ✅
T3: Android 系统启动
T4: Framework 读取 APN 数据库
T5: Framework 评估并更新 Modem 中的 APN(如有需要)
如果没有 Modem 存储:
- T2 时刻,Modem 无法附着到网络 ❌
- 用户在 T2-T5 期间无法接收短信和来电 ❌
6.2 场景 2:飞行模式快速切换
// 用户:飞行模式 ON → OFF
1. Modem 重新启动
2. Modem 立即使用存储的 APN 附着到网络 ✅
3. 用户几秒内就能上网
如果没有 Modem 存储:
- Modem 需要等待 Android Framework 重新配置 APN
- 用户体验延迟 5-10 秒 ❌
6.3 场景 3:紧急呼叫(Emergency Call)
// DataProfileManager.java
// 添加默认 EIMS APN
if (dataProfile == null) {
profiles.add(new DataProfile.Builder()
.setApnSetting(buildDefaultApnSetting("DEFAULT EIMS", "sos",
ApnSetting.TYPE_EMERGENCY))
.setTrafficDescriptor(new TrafficDescriptor("sos", null))
.build());
log("Added default EIMS data profile.");
}
Emergency APN (sos):
- 必须存储在 Modem 中
- 即使 Android 系统崩溃,Modem 也能建立紧急连接
- 用于紧急呼叫的 IMS 注册
7. 同步机制
7.1 Android → Modem 同步
// DataProfileManager.java
private void updateDataProfiles(boolean forceUpdateIa) {
// 1. 从数据库读取所有 APN
List<DataProfile> profiles = new ArrayList<>();
Cursor cursor = mPhone.getContext().getContentResolver().query(...);
// 2. 更新 Initial Attach APN
updateInitialAttachDataProfileAtModem(forceUpdateIa);
// 3. 更新所有 Data Profile 到 Modem
updateDataProfilesAtModem();
}
同步时机:
- SIM 卡插入
- APN 数据库变化
- 运营商配置更新
- 用户手动修改 APN
7.2 Preferred Data Profile 同步
// DataProfileManager.java
private void setPreferredDataProfile(@Nullable DataProfile dataProfile) {
logl("setPreferredDataProfile: " + dataProfile);
// 1. 保存到 Android 数据库
Uri uri = Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI, subId);
resolver.insert(uri, values);
// 2. 标记为 preferred
dataProfile.setPreferred(true);
// 3. 同步到 Modem
updateDataProfilesAtModem();
}
8. 存储限制和优化
8.1 Modem 存储容量限制
典型限制:
- 最多存储 10-20 个 Data Profile
- Initial Attach APN:1 个
- Emergency APN:1 个
- 其他常用 APN:5-10 个
8.2 优先级策略
// DataProfileManager.java
// 排序优先级:preferred > IA > IMS > emergency > default
List<DataProfile> allDataProfiles = mAllDataProfiles.stream()
.sorted(Comparator.comparing((DataProfile dp) -> !dp.equals(mPreferredDataProfile)))
.toList();
Modem 只存储:
- ✅ Initial Attach APN
- ✅ Preferred APN
- ✅ IMS APN(VoLTE)
- ✅ Emergency APN
- ✅ 最近成功连接的 APN
9. 总结
9.1 为什么需要双层存储?
| 存储位置 | 优势 | 劣势 | 用途 |
|---|---|---|---|
| Android Framework | 灵活、易修改、容量大 | 依赖系统启动 | 管理所有 APN,动态选择 |
| Modem | 独立、快速、开机即用 | 容量有限、修改困难 | Initial Attach、紧急场景 |
9.2 关键设计原则
- 快速响应:Modem 存储确保开机后立即可用
- 容错性:即使 Android 崩溃,Modem 仍能工作
- 紧急保障:Emergency APN 必须在 Modem 中
- 同步一致性:Android 定期同步关键 APN 到 Modem
9.3 核心要点
┌─────────────────────────────────────────────────────┐
│ 为什么 Modem 需要存储 APN? │
│ │
│ 1. ⚡ Initial Attach 需要 │
│ • 开机时 Android 未启动,Modem 需要立即附着 │
│ │
│ 2. 🚨 紧急场景需要 │
│ • 系统崩溃时仍能紧急呼叫 │
│ │
│ 3. 🚀 快速恢复需要 │
│ • 飞行模式切换快速重连 │
│ │
│ 4. 🔄 网络切换需要 │
│ • 网络重选时快速建立连接 │
└─────────────────────────────────────────────────────┘
希望这个详细解析帮助您深入理解为什么 Modem 中也需要存储 APN!这是 Android Telephony 架构中的一个重要设计决策。🚀