1. 数据卡顿恢复管理器 (DataStallRecoveryManager)
这是电话数据连接中最重要的恢复机制:
恢复策略(4 个递进步骤)
初始状态:数据连接卡顿(无法访问网络)
↓
步骤 1: RECOVERY_ACTION_GET_DATA_CALL_LIST
├─ 查询 RIL 获取当前数据连接状态
├─ 检查 IP 地址、DNS 等链路信息
└─ 目的:检测是否只是状态不同步
↓
步骤 2: RECOVERY_ACTION_CLEANUP
├─ 断开数据连接 (RIL_REQUEST_DEACTIVATE_DATA_CALL)
├─ 立即重新建立连接 (RIL_REQUEST_SETUP_DATA_CALL)
└─ 目的:重新建立 RIL 和 Modem 之间的通道
↓
步骤 3: RECOVERY_ACTION_RADIO_RESTART
├─ 关闭无线电 (RIL_REQUEST_RADIO_POWER)
├─ 重新开启无线电
└─ 目的:让 Modem 重新附加到网络
↓
步骤 4: RECOVERY_ACTION_RESET_MODEM
├─ 重启 Modem (NV_RESET_CONFIG)
└─ 目的:解决 Modem 端的问题
关键代码(doRecovery 方法)
private void doRecovery() {
@RecoveryAction final int recoveryAction = getRecoveryAction();
final int signalStrength = mPhone.getSignalStrength().getLevel();
// 记录事件
TelephonyMetrics.getInstance()
.writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
mLastAction = recoveryAction;
switch (recoveryAction) {
case RECOVERY_ACTION_GET_DATA_CALL_LIST:
logl("doRecovery(): get data call list");
getDataCallList(); // 步骤 1
setRecoveryAction(RECOVERY_ACTION_CLEANUP);
break;
case RECOVERY_ACTION_CLEANUP:
logl("doRecovery(): cleanup all connections");
cleanUpDataNetwork(); // 步骤 2
setRecoveryAction(RECOVERY_ACTION_RADIO_RESTART);
break;
case RECOVERY_ACTION_RADIO_RESTART:
logl("doRecovery(): restarting radio");
setRecoveryAction(RECOVERY_ACTION_RESET_MODEM);
powerOffRadio(); // 步骤 3
break;
case RECOVERY_ACTION_RESET_MODEM:
logl("doRecovery(): modem reset");
rebootModem(); // 步骤 4
resetAction();
mIsAttemptedAllSteps = true;
break;
}
startNetworkCheckTimer(mLastAction);
}
恢复原因追踪
// 系统追踪数据恢复的原因
public @interface RecoveredReason {}
private static final int RECOVERED_REASON_NONE = 0; // 未恢复
private static final int RECOVERED_REASON_DSRM = 1; // DSRM 恢复
private static final int RECOVERED_REASON_MODEM = 2; // Modem 自己恢复
private static final int RECOVERED_REASON_USER = 3; // 用户手动恢复
2. 数据库损坏恢复 (DefaultDatabaseErrorHandler)
处理 SQLite 数据库腐蚀的机制:
工作流程
数据库腐蚀被检测到
↓
onCorruption() 被调用
↓
判断数据库是否已打开?
├─ NO (未打开)
│ └─ 直接删除数据库文件
│ (文件无法打开说明已严重损坏)
│
└─ YES (已打开)
├─ 获取附加数据库列表
├─ 关闭数据库
└─ 删除主数据库和所有附加数据库
(防止残留的腐蚀文件影响后续操作)
关键代码
public void onCorruption(SQLiteDatabase dbObj) {
Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
SQLiteDatabase.wipeDetected(dbObj.getPath(), "corruption");
// 检查数据库是否打开
if (!dbObj.isOpen()) {
// 数据库文件无法打开,直接删除
deleteDatabaseFile(dbObj.getPath());
return;
}
List<Pair<String, String>> attachedDbs = null;
try {
// 获取所有附加数据库
try {
attachedDbs = dbObj.getAttachedDbs();
} catch (SQLiteException e) {
/* 数据库太损坏,甚至无法执行此命令 */
}
// 关闭数据库
try {
dbObj.close();
} catch (SQLiteException e) {
/* 忽略错误继续 */
}
} finally {
// 删除所有文件
if (attachedDbs != null) {
// 删除所有附加数据库
for (Pair<String, String> p : attachedDbs) {
deleteDatabaseFile(p.second);
}
} else {
// 删除主数据库
deleteDatabaseFile(dbObj.getPath());
}
}
}
private void deleteDatabaseFile(String fileName) {
if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
return; // 不删除内存数据库
}
Log.e(TAG, "deleting the database file: " + fileName);
try {
SQLiteDatabase.deleteDatabase(new File(fileName), false);
} catch (Exception e) {
Log.w(TAG, "delete failed: " + e.getMessage());
}
}
3. 应用数据备份和恢复
系统中有多层备份恢复机制:
完整备份恢复 (FullRestoreEngine)
备份数据存在
↓
应用安装
↓
FullRestoreEngine.onRestore()
├─ 读取备份数据流
├─ 启动应用 Agent
├─ 逐块传递数据给应用
└─ 应用重建其数据状态
关键特性:
- 应用负责清除旧数据
- 如果恢复失败,OS 会自动清除应用数据
- 支持 OBB (Opaque Binary Blob) 数据恢复
关键 API
public abstract void onRestore(
BackupDataInput data, // 备份数据输入流
int appVersionCode, // 备份时的应用版本
ParcelFileDescriptor newState // 新的恢复状态文件
) throws IOException;
4. 网络数据恢复 (NetworkStatsRecorder)
处理网络统计信息的恢复:
故障恢复策略
FileRotator 失败
↓
recoverAndDeleteData()
├─ 将状态转储到 DropBoxManager(用于调试)
└─ 如果设置了 mWipeOnError
└─ 删除所有数据文件并重新开始
void recoverAndDeleteData() {
if (DUMP_BEFORE_DELETE) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
mRotator.dumpAll(os); // 转储状态用于调试
} catch (IOException e) {
os.reset();
} finally {
IoUtils.closeQuietly(os);
}
mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
}
// 如果设置了自动清除错误数据
if (mWipeOnError) {
mRotator.deleteAll(); // 删除所有文件
}
}
5. 系统恢复 (RescueParty)
系统级别的故障恢复机制:
恢复级别 (Rescue Levels)
级别 1: 重置受信设置
└─ 重置系统默认配置
级别 2: 清除应用缓存
└─ 删除应用缓存数据
级别 3: 清除应用数据
└─ 完全清除应用用户数据
级别 4: 恢复出厂设置
└─ 清空用户数据,恢复系统默认状态
工作流程
应用多次崩溃或系统故障
↓
RescueParty 监测到
↓
尝试当前恢复级别
├─ 成功?
│ └─ 记录事件,恢复完成
│
└─ 失败?
└─ 升级到更激进的恢复级别
↓
最终重启到恢复模式
6. 数据恢复的分层策略
| 层级 | 恢复对象 | 恢复方式 | 何时触发 |
|---|---|---|---|
| 1. 轻量级 | 数据连接 | 重新查询、清除、重启无线电 | 数据卡顿 |
| 2. 中等级 | 数据库 | 检测腐蚀,删除文件,让应用重建 | 数据库打开失败 |
| 3. 应用级 | 应用数据 | 从备份恢复数据 | 应用安装、升级 |
| 4. 网络级 | 网络统计 | 删除损坏的统计数据 | 文件操作失败 |
| 5. 系统级 | 整个系统 | 清除缓存、应用数据、恢复出厂 | 系统故障、多次崩溃 |
7. 关键设计原则
1. 渐进式恢复 (Progressive Recovery)
├─ 先尝试轻量级操作
├─ 逐步升级到更激进的措施
└─ 避免不必要的数据丢失
2. 错误隔离 (Error Isolation)
├─ 单个组件故障不影响整体系统
├─ 数据库腐蚀只删除该数据库
└─ 应用崩溃不清除其他应用数据
3. 用户控制 (User Control)
├─ 用户可以手动恢复(如关闭/打开数据)
├─ 自动恢复前通常会通知用户
└─ 系统记录所有恢复事件用于日志
4. 数据保留 (Data Retention)
├─ 删除前先转储数据用于调试
├─ 保存在 DropBox 供后续分析
└─ 最后才是真正删除
5. 可监控性 (Observability)
├─ 记录所有恢复事件
├─ 追踪恢复的原因和时间
└─ 使用 LocalLog 进行详细日志
8. 实际应用场景
| 场景 | 触发恢复 | 恢复机制 |
|---|---|---|
| 用户无法上网 | ✓ 数据卡顿 | DSRM → 步骤 1-4 |
| 系统启动缓慢 | ✓ 应用缓存损坏 | 清除缓存 |
| 应用频繁崩溃 | ✓ 系统故障 | RescueParty 恢复级别 |
| 数据库文件损坏 | ✓ 数据库操作失败 | 删除损坏文件 |
| 网络统计异常 | ✓ 文件操作错误 | 删除统计数据重新计算 |
| OTA 更新失败 | ✓ 更新过程错误 | 回滚到前一版本或恢复 |
这个多层次、多策略的数据恢复机制确保了 Android 系统的鲁棒性和用户体验。