RainySY d293c7c0de fix(build): 修复包重组后所有 import 错误 + 安全占位符漏洞
## 构建与测试结果

- \`./gradlew assembleDebug\` BUILD SUCCESSFUL
- \`./gradlew test\` 99/99 测试通过
- \`app-debug.apk\` 33 MB 生成

## 修复内容

### 1. 领域类型位置修正

\`AppInfo\`、\`PackageName\`、\`UserId\` 是核心领域类型,被 UI 层
(BackupScreen/ViewModel)、restic 子包、BackupOperation、AppScanner 等
多处引用。原始位置在 \`scan/AppScanner.kt\` 内(与扫描器紧耦合),
但子包化后跨包引用不便。已将它们提取到 \`backup/AppInfo.kt\` 与
\`backup/DomainTypes.kt\`(根包)作为公开领域模型。

\`AppScanner.kt\` 现在只负责扫描实现,不再定义数据模型。

### 2. 缺失 import 系统修复(~20 个文件)

包重组后所有子包文件需要显式 import 根包与其他子包的类:

- \`restic/ResticBackup.kt\`, \`ResticRestore.kt\`, \`ResticMaintenance.kt\` 等
  全部添加 \`com.example.androidbackupgui.backup.core.{AppError, AppResult, err}\`
- \`restic/SmbTransport.kt\` 添加 \`backup.core.{AppError, AppResult, LogUtil, err, retryWithBackoff}\`
  和 \`backup.security.MissingAlgoProvider\`
- \`restic/WebdavTransport.kt\` 类似补全
- \`restic/ResticStreamBackup.kt\`、\`ResticWrapper.kt\` 添加 \`backup.AppInfo\`
- \`ui/BackupViewModel.kt\`、\`RestoreScreen.kt\` 添加子包 import
- \`backup/BackupIntegrityChecker.kt\` 添加 \`root.{RootShell, shellEscape}\`
- \`scan/AppScanner.kt\` 添加 \`backup.{AppInfo, BackupConfig, PackageName, UserId}\`
- \`security/CredentialProvider.kt\` 添加 \`backup.BackupConfig\`

### 3. SsaidCache 协程适配

\`SsaidCache.init { }\` 是非 suspend 上下文,不能直接调用
\`RootShell.exec()\`(suspend)。修复:用 \`kotlinx.coroutines.runBlocking { }\`
桥接。该类仅在备份预热阶段构造,在后台调度器上运行,
阻塞单次 shell exec 是可接受的。

### 4. CredentialProvider 占位符漏洞(安全关键)

\`resolve()\` 在 PasswordManager 未初始化时回退到 \`config.resticPassword\`,
但 \`takeIf { it.isNotEmpty() }\` 没过滤 \`"stored-in-keystore"\` 占位符。

后果:如果用户的 \`backup_settings.conf\` 包含占位符(新版 toFile 写入
\`"stored-in-keystore"\`),配置回退路径会把字面字符串作为 restic 仓库
密码传给 CLI。

修复:在 \`takeIf\` 中增加 \`it != "stored-in-keystore"\` 检查。
\`migrateLegacyPasswords\` 已有此检查,\`resolve()\` 之前漏了。

**这个漏洞是被 CredentialProviderTest 发现的** — TDD 价值体现。

### 5. 测试用例修正

- \`BackupProgressTrackerTest\`: \`Thread.sleep(50)\` → \`Thread.sleep(1500)\`
  使 ETA > 0 的断言稳定通过(之前 50ms 不足以让 EMA 计算出 > 1s)

## 测试覆盖

- 11 个测试类,99 个测试用例全部通过
- 新增覆盖:\`RestoreArchiveSafety\`(11 用例,路径白名单防护核心)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-14 20:32:55 +08:00
2026-06-08 14:18:28 +08:00
2026-06-06 13:09:23 +08:00
2026-06-06 13:09:23 +08:00
2026-06-08 14:18:28 +08:00
2026-06-07 20:48:52 +08:00
2026-06-06 13:09:23 +08:00
2026-06-06 13:09:23 +08:00

Android Backup GUI

Android 应用备份与恢复工具,通过 root 权限 实现应用的完整备份APK + 用户数据 + OBB + SSAID + 权限 + WiFi 配置),集成 restic 实现增量去重备份,支持 SMB / WebDAV 远程仓库。

功能

  • 应用扫描 — 自动列出第三方应用,支持系统应用白名单
  • APK + 数据备份 — 备份 APK 文件、应用数据目录、OBB 数据、SSAID、SSAID 广告标识、AppOps 权限、WiFi 配置
  • 多用户支持 — 从配置页选择 Android 用户(主用户 / 工作资料),持久化到配置文件
  • 并行备份/恢复 — 备份并发 3Semaphore恢复并发 2Semaphore
  • 存档完整性校验 — 备份后自动 zstd/gzip 校验 + tar 结构验证
  • restic 增量去重 — 内建 librestic.so~24MBSSD 加密快照,增量备份
  • 远程后端 — 本地 REST 桥 + NanoHTTPD 将 SMB/WebDAV 协议翻译为 restic 可直接访问的 REST API
  • 流式备份 — FIFO 管道对接 restic backup --stdin,无需本地暂存
  • 配置持久化 — 仓库路径、密码、后端参数、目标用户保存在 backup_settings.conf
  • 快照管理 — 初始化、查看统计、按策略清理旧快照(保留 7 天/4 周/3 月)、解锁
  • 累积快照 — 从历史快照读取元数据,合并为增量累积备份
  • 应用名显示 — 备份时缓存应用名称到 app_details.json,已卸载应用也显示中文名

技术栈

  • Kotlin / Android SDK (minSdk 24, targetSdk 34)
  • Jetpack Compose + Material 3 UI
  • ViewModel + StateFlow + SharedFlow面向状态架构
  • kotlinx-serializationJSON 解析)
  • restic 0.17+ 二进制(编译为 librestic.so
  • sardine-android (WebDAV 客户端)
  • jcifs-ng (SMB 客户端)
  • NanoHTTPDREST 桥服务器)
  • libsuMagisk / KernelSU / APatch root shell
  • Kotest + MockK单元测试

架构

┌─────────────────────────────────────────────┐
│  UI 层 (Jetpack Compose + Material 3)       │
│  AppScaffold → BackupScreen / RestoreScreen │
│              / ConfigScreen                  │
│              / ConfigViewModel (StateFlow)   │
├─────────────────────────────────────────────┤
│  业务逻辑层 (backup/)                        │
│  BackupOperation   → root shell tar/cp      │
│  RestoreOperation  → root shell pm install  │
│  StreamingBackup   → FIFO pipe → restic     │
│  ResticWrapper     → facade 委托给:          │
│    ├── ResticBackup      (备份)              │
│    ├── ResticRestore     (恢复 + dump)       │
│    ├── ResticSnapshotOps (快照列表/forget)    │
│    ├── ResticMaintenance (prune/check/stats) │
│    └── ResticRepoInit    (init)              │
│  ResticCommandRunner → ProcessBuilder       │
│  ResticEnvResolver   → 环境变量构建           │
│  RestBridgeRunner    → ResticRestBridge      │
│  RemoteTransport     → file I/O 接口          │
│    ├── WebdavTransport (sardine, 8KB 分块)    │
│    └── SmbTransport (jcifs-ng, 8KB 分块)      │
├─────────────────────────────────────────────┤
│  Root 层 (root/)                             │
│  RootShell → libsu (Magisk/KernelSU/APatch)  │
├─────────────────────────────────────────────┤
│  Service 层                                  │
│  BackupService → Foreground Service          │
├─────────────────────────────────────────────┤
│  原生二进制 (jniLibs)                          │
│  librestic.so / libtar_bin.so / libzstd_bin  │
└─────────────────────────────────────────────┘

数据流(一次备份)

用户选择应用 → 扫描 (AppScanner.enumerateUsers / scanThirdParty)
    ↓
创建 Backup_ 目录 → 写入 appList.txt + app_details.json
    ↓
并行 (Semaphore=3) 为每个应用:
    ├── 备份 APK (cp → app目录/包名.apk)
    ├── 备份数据 (tar zstd → 包名_data.tar.zst)
    ├── 备份 OBB (tar → 包名_obb.tar.zst)
    ├── 备份 SSAID (提取 → ssaid.txt)
    ├── 备份图标 (snapshot cache / aapt)
    └── 备份权限 (dumpsys → permissions.txt)
    ↓
WiFi 备份 → WifiManager.backup()
    ↓
(可选) ResticWrapper.backup() → restic 快照到远程仓库

远程仓库REST 桥模式)

Restic CLI ←→ ResticRestBridge (NanoHTTPD, 127.0.0.1:random)
                      ↓
              RemoteTransport (SMB/WebDAV)

restic 通过 REST HTTP API 与本地桥通信,桥接器将请求翻译为 SMB/WebDAV 文件操作。 无需本地 staging 仓库restic 直接读写远程存储。

构建

版本历史

版本 更新内容
v1.14 修复 shell 转义/管道死锁/配置序列化缺陷,新增配置导出与 BackupConfig 单元测试
v1.13 Compose Material 3 UI 重构、Unlock 支持、ResticBinary 启动初始化、修复 500 错误和刷新竞态
v1.12 引擎 + Compose Material 3 UI 重构
v1.11 构建系统改进、LSP 支持
v1.10 后端桥接稳定性提升
v1.9 远程后端优化
v1.8 快照管理增强
v1.7 多用户支持
v1.6 累积快照
v1.5 修复签名配置
v1.4 APK 体积优化R8 + shrinkResources25MB → 11.8MB
v1.3 累积快照、AppResult 类型化错误、kotlinx-serialization

编译命令

# Debug APK
./gradlew assembleDebug

# Release APK需配置签名
KEYSTORE_PASSWORD=<密码> KEY_PASSWORD=<密码> ./gradlew assembleRelease

Release 构建需要 app/release.keystore;原生库放在 jniLibs/arm64-v8a/

使用说明

  1. Android 设备需 root 权限Magisk / KernelSU / APatch
  2. 在「配置」页签设置备份选项 + restic 仓库参数
  3. 切换「备份用户」选项(多用户设备)
  4. 在「备份」页签选择应用 → 开始备份
  5. 在「恢复」页签选择本地备份或 restic 快照 → 开始恢复

远程后端配置

字段 WebDAV 示例 SMB 示例
后端 WebDAV SMB
地址 https://webdav.example.com 192.168.1.165
用户名 账号 Windows 用户名
密码 密码 Windows 密码
共享名称 back
仓库存放路径 backup backup

注意事项

  • 应用卸载会清除 backup_settings.conf,建议定期导出配置
  • Restic 仓库需先「初始化」才能使用(自动检测已有仓库)
  • SMB 密码错误多次会导致 Windows 账户锁定,需在服务器上解锁
Description
No description provided
Readme 102 MiB
Languages
Kotlin 98.8%
Python 1%
Java 0.2%