先看结论与判断条件

  • 升级测试的起点是仍在用户设备上的真实旧版本与状态,不是任意历史 APK;每条路径要绑定旧包和加固候选的 SHA-256。
  • 平台更新资格至少涉及 applicationId、可接受的 versionCode 和兼容签名关系,安装成功只证明平台门槛通过,不证明业务迁移正确。
  • 上传密钥、应用签名密钥、证书和 Play App Signing 责任必须区分,本地重签名候选不能冒充商店最终签名对象。
  • 升级前要构造版本化数据契约,覆盖数据库、偏好、文件、账号、密钥引用、队列和功能开关,升级后验证语义而非只比文件数量。
  • 覆盖安装会进入新的进程与初始化路径,应检查组件、Provider、Native 库、后台任务、异常恢复和首次启动,而不是沿用旧进程观察。
  • 云真机矩阵应从真实支持范围和版本分布选择;单一设备、单一旧版本或安装器返回成功都不能代表升级兼容完成。

升级回归必须从真实旧状态出发,不能用全新安装替代

未加固旧版本升级到加固版本的风险不只在安装阶段。旧版本已经留下数据库、偏好、文件、账号令牌、Keystore 引用、任务记录和功能状态;加固候选还可能改变类加载、启动组件、Native 载荷和异常路径。卸载后重装会清除关键历史状态,最容易漏掉的迁移问题反而全部消失。

每个升级场景应由 oldCandidate、newCandidate、stateFixture 和 expectedContract 四个对象组成。oldCandidate 必须是实际发布过或仍受支持的版本,newCandidate 是准备交付的精确加固文件,stateFixture 描述升级前业务状态,expectedContract 规定升级后哪些数据保留、转换、失效或要求重新登录。

测试顺序是安装旧包、运行到指定状态、停止进程、就地覆盖新包、启动新进程并验证契约。过程中不得清数据,也不能用调试菜单跳过迁移。若必须调整测试环境,应在场景定义中提前声明并让基线与候选一致,不能遇到失败才临时清理。

升级路径的四个固定对象
对象内容身份字段失败条件
旧候选真实未加固版本APK 摘要、versionCode、证书来源不明或已重签
新候选待验收加固版本APK 摘要、配置、签名阶段与交付文件不同
状态夹具账号、数据与业务进度fixtureId 与 schema人工状态不可复现
结果契约保留、迁移、失效规则contractVersion只检查能否启动
设备环境系统、ABI、locale 与安装器deviceIdHash 与构建环境中途变化

先验证 applicationId、versionCode 与更新身份

How Android app updates work 说明,应用更新需要保持 applicationId,使用可接受的 versionCode,并满足兼容签名或轮换证明等条件。这些是平台识别同一应用并允许覆盖更新的基础。若包名变化,系统会把它视为另一应用;若版本关系不被接受,安装器会拒绝正常升级。

安装资格门禁应在业务测试前执行。记录旧包和新包的 applicationId、versionCode、证书信息与文件摘要,再执行覆盖安装并保存安装器原始结果。不要根据文件名、市场版本名或构建目录推断身份。相同 versionName 也可能对应不同 versionCode 与不同二进制。

平台允许安装不等于业务防重放、服务端版本策略或数据迁移已经正确。本文只处理旧版到新版的前向升级,不把降级重放与证书轮换策略混入同一结论。需要这些能力时应建立独立威胁模型、授权规则和验收路径。

覆盖升级前的身份门禁
字段要求证据不能推出
applicationId前后属于同一应用身份产物解析与安装结果业务账号连续
versionCode满足目标渠道更新规则前后产物元数据迁移脚本正确
签名关系满足平台兼容要求证书与轮换材料密钥保管安全
APK 摘要锁定精确文件SHA-256 回执该文件已发布
安装器结果覆盖会话成功或明确失败原始状态和消息核心路径通过

签名连续性要区分上传密钥与最终应用签名

Sign your Android app 区分应用签名密钥、上传密钥、证书和 Play App Signing 的责任。开发团队上传的 AAB 可以由上传密钥签名,而用户设备收到的 APK 由应用签名密钥签名。因此,本地拿上传密钥生成的 APK 不能代表商店最终更新对象。

升级矩阵要标注 signingStage。若验证本地直接分发 APK,应使用对应渠道允许的签名关系;若验证 Play 路径,应从受控测试轨道获得最终派生 APK 或真实安装回执。证书摘要只是身份事实的一部分,不能证明私钥保管、服务端发布授权或加固效果。

遇到签名不兼容时,不能通过卸载旧包后安装新包来宣称问题解决,因为这改变了用户数据和应用身份连续性。正确结果是阻断该升级候选,核对发布链、历史证书和平台支持的签名关系。任何涉及密钥的材料都应保留在受控系统,公开脚本只记录证书摘要。

签名阶段与升级证据
阶段对象可用证据误用
本地调试调试 APK受控诊断记录冒充商业升级
直接分发渠道签名 APK证书和安装回执套用 Play 结论
Play 上传上传签名 AAB上传文件和轨道记录当作设备最终证书
Play 分发应用签名 APK真实安装或派生回执用本地重签包替代
证书摘要公开身份指纹前后候选记录暴露私钥或证明保护强度

Split 与 PackageInstaller 会话也要保持候选一致

Android PackageInstaller 文档说明 split 安装中的 base、packageName、versionCode 和签名证书需要保持一致。AAB 分发场景下,设备可能收到 base 与多个配置或功能 split。只抽查 base APK,无法证明完整安装会话中所有 split 都属于同一版本和签名集合。

升级证据应记录设备实际安装的包集合、ABI、语言、密度、动态模块状态和安装来源。若旧版本曾下载按需模块,新版本升级后要验证模块状态与业务数据是否合理延续。平台 API 约束可以拒绝明显不一致的会话,却不能证明 Play 服务器所有设备组合都已被团队抽样。

工程判断是为代表设备保存 oldInstalledSetDigest 与 newInstalledSetDigest,并把每条升级用例绑定 device spec。通用 APK、本地 `.apks` 和线上派生 APK 不能混为同一对象。若 split 集变化,测试要解释这是版本设计、设备选择还是构建异常。

Split 升级需要核对的集合字段
字段作用异常信号处理
base identity定义包和版本基础base 与 split 不同版本拒绝会话
split names标识配置与功能模块缺失已用模块验证交付状态
ABI 与配置匹配设备选择安装错误架构核对 device spec
签名证书维持会话身份一致split 证书不一致阻断候选
installed set digest锁定设备实际集合回执无法对应重新采集

数据迁移要验证业务语义,不只确认文件还在

升级前数据应按业务域构造,而不是随手操作几步。至少考虑数据库 schema 与关键记录、SharedPreferences、内部文件、账号与会话、Keystore alias 引用、待处理任务、功能开关和下载资源。每项都要定义 preserve、migrate、invalidate 或 regenerate,避免测试人员凭肉眼判断。

文件存在不代表语义正确。数据库可能完成 schema 升级却丢失约束,偏好键可能保留但类型改变,密文可能存在却因别名、认证条件或序列化变化无法读取。验证应通过公开业务接口或受控测试接口读取规范化结果,不直接把内部字节相等当作成功。

迁移还要覆盖中断和幂等。若应用在迁移中被终止,再次启动不能重复扣减、重复提交或进入半完成状态。工程判断是保存 migrationState、fromSchema、toSchema、completedSteps 与 recoverableError,并测试首次升级、迁移中断后恢复和升级完成后的再次启动。

升级数据契约的最小分类
数据域预期动作验证方法失败示例
数据库迁移 schema 与记录业务查询和约束表存在但字段语义错误
偏好与配置保留或转换键值类型化读取旧类型解析失败
账号与会话按策略延续或失效鉴权状态与刷新路径静默退出或错误复用
密钥引用保持别名或明确重建受控加解密契约密文在但无法读取
任务与文件恢复、重排或清理幂等和状态机重复执行或永久卡住

覆盖安装后必须从新进程验证初始化和核心路径

覆盖更新会使后续运行进入新候选代码和新的进程初始化。测试应在升级前停止旧进程,完成安装后重新启动,确认 Application、ContentProvider、组件、Native 库和保护载荷按新版本建立。不能让旧进程或测试框架缓存掩盖类加载、符号或初始化问题。

Android instrumented tests 在真实 Android 环境执行,可访问组件和系统 API,适合验证迁移后的数据库、权限、文件、Keystore、Service、Provider 和核心业务路径。测试应区分安装前状态准备、升级后首次启动、核心读写、后台恢复和再次启动,每一阶段都绑定同一新候选。

核心路径要按风险选择,包括登录续期、离线数据读取、授权判断、关键交易状态、推送或深链入口以及后台任务。本文不声称这些路径已由任何产品自动覆盖。具体集合取决于应用资产和失败成本,未提供项目证据时只能列为待验证。

升级后分阶段验收
阶段主要检查证据阻断条件
安装完成包、版本、签名与 split安装器和包信息身份不连续
首次启动迁移、组件和 Native 加载事件、日志与断言退出或半迁移
核心读写业务数据语义instrumented test状态丢失或误用
后台恢复任务、Service 与进程重建设备运行回执重复或永久暂停
再次启动幂等与稳定状态第二次契约结果迁移重复执行

退出证据和设备矩阵决定结论能覆盖多远

Android ApplicationExitInfo 可提供历史进程退出原因、ANR trace,并在适用新版本提供 Native tombstone。升级首次启动若发生闪退、ANR 或系统终止,应将记录与旧候选、新候选、安装完成时间、caseId 和用户路径关联。退出原因不能脱离 trace、堆栈和迁移状态直接当作根因。

Firebase Test Lab Android matrices 由设备型号、OS、方向和 locale 等维度组成,任一执行失败都会影响矩阵判断。升级矩阵还要增加旧版本和状态夹具维度。设备选择应来自最低支持范围、真实版本分布和高风险厂商,而不是只挑当前可用的最新设备。

云真机结果仍有边界。它不能自动复现全部安装来源、厂商账号、推送环境或真实用户历史数据。工程判断是先在可控矩阵跑确定状态,再对关键渠道执行小范围真实升级验证。每个失败都要保留安装、启动、退出和数据契约证据,不能用其他设备通过抵消。

  • 退出记录绑定升级时间窗和新候选摘要
  • ANR trace、Java 堆栈与 Native tombstone 分层解释
  • 设备矩阵包含真实支持 OS 与 ABI
  • 旧版本选择来自实际分布和支持策略
  • locale、方向和设备状态明确
  • 任一执行失败单独处置,不被平均掩盖
  • 云矩阵结论不外推所有安装渠道

用模拟器脚本执行不清数据的就地升级骨架

下面的 Bash 示例只允许在 Android 模拟器运行。它接收旧 APK、新 APK、包名、启动组件、instrumentation runner 和空证据目录,记录两份文件摘要,安装旧包并执行 before 契约,再使用 `adb install -r` 就地升级,最后执行 after 契约和包信息采集。

脚本不卸载应用、不清数据、不读取密钥,也不在真实设备运行。任何安装、启动、instrumentation 或包身份检查失败都会返回非零状态。测试 APK 与状态夹具需由项目另行准备;脚本只是公开安全的执行骨架,不能替代签名解析、商店轨道回执和业务断言设计。

准备未加固到加固版本的升级评估时,可整理真实旧版本、加固候选、签名与 split 身份、状态夹具、迁移契约、首次启动、核心路径、退出记录和设备矩阵,再通过御盾中央平台提交申请。脚本通过不等于项目升级兼容已经完成。

  • 脚本仅在单一 Android 模拟器运行
  • 旧包与新包摘要先于安装保存
  • 覆盖升级使用 -r 且不卸载清数据
  • 升级前后均执行版本化数据契约
  • 包身份、启动和 instrumentation 失败即阻断
  • 证据目录必须是新的空目标
  • 模拟器结果不冒充商店或真实设备发布回执
仅在模拟器执行旧包到加固包的就地升级骨架
#!/usr/bin/env bash
set -euo pipefail

if [ "$#" -ne 6 ]; then
  exit 2
fi
baseline_apk=$1
hardened_apk=$2
package_name=$3
start_component=$4
test_runner=$5
evidence_dir=$6
if [ ! -f "$baseline_apk" ] || [ ! -f "$hardened_apk" ] || [ -e "$evidence_dir" ]; then
  exit 2
fi
device_count=$(adb devices | awk 'NR > 1 && $2 == "device" {count += 1} END {print count + 0}')
if [ "$device_count" -ne 1 ]; then
  exit 2
fi
is_emulator=$(adb shell getprop ro.boot.qemu | tr -d '\r')
if [ "$is_emulator" != "1" ]; then
  exit 2
fi
mkdir -p "$evidence_dir"
shasum -a 256 "$baseline_apk" > "$evidence_dir/baseline.sha256"
shasum -a 256 "$hardened_apk" > "$evidence_dir/hardened.sha256"
adb install "$baseline_apk" | tee "$evidence_dir/install-baseline.txt"
if ! adb shell cmd package list packages | grep -Fx "package:$package_name" > "$evidence_dir/package-before.txt"; then
  exit 3
fi
adb shell am force-stop "$package_name"
adb shell am start -W -n "$start_component" > "$evidence_dir/start-before.txt"
adb shell am instrument -w -e upgrade_phase before "$test_runner" > "$evidence_dir/contract-before.txt"
adb shell dumpsys package "$package_name" > "$evidence_dir/dumpsys-before.txt"
adb shell am force-stop "$package_name"
adb install -r "$hardened_apk" | tee "$evidence_dir/install-upgrade.txt"
if ! adb shell cmd package list packages | grep -Fx "package:$package_name" > "$evidence_dir/package-after.txt"; then
  exit 3
fi
adb shell am start -W -n "$start_component" > "$evidence_dir/start-after.txt"
adb shell am instrument -w -e upgrade_phase after "$test_runner" > "$evidence_dir/contract-after.txt"
adb shell dumpsys package "$package_name" > "$evidence_dir/dumpsys-after.txt"
printf '%s\n' "upgrade-evidence-complete"

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
Android 更新需要保持 applicationId,使用可接受的 versionCode,并满足兼容签名或轮换证明条件。How Android app updates work 描述应用更新身份与版本条件。平台更新条件不证明业务数据迁移、服务端防重放或核心路径正确。
发布流程需要区分应用签名密钥、上传密钥、证书和 Play App Signing 责任。Sign your Android app 描述 Android 应用签名与 Play App Signing 流程。文档不能确认某个实际候选使用正确生产证书,也不能证明密钥保管安全。
Split 安装要求 base、packageName、versionCode 和签名证书保持一致。Android PackageInstaller 描述安装会话和 split APK 的一致性约束。API 约束不能证明 Play 服务器生成的全部设备 split 组合已被项目抽样。
依赖真实 Android 运行时、组件和系统 API 的升级后语义可以通过设备端测试验证。Android instrumented tests 说明 instrumented test 在真实 Android 环境执行。单一设备通过不能代表完整 API、ABI、系统版本和厂商矩阵。
历史进程退出信息可以提供退出原因、ANR trace,并在适用版本提供 Native tombstone。Android ApplicationExitInfo 描述进程退出记录和诊断材料。退出记录仍需关联同一候选、升级时间窗和用户路径,不能单独确定根因。
云真机矩阵由设备、OS、方向和 locale 等维度组成,执行失败会影响矩阵判断。Firebase Test Lab Android matrices 描述 Android 测试矩阵的配置与结果。云设备仍需按真实用户和最低支持范围选择,不能覆盖全部渠道与历史状态。
未加固旧版本到加固版本必须执行不清数据的就地覆盖升级。工程判断:卸载重装会消除数据库、偏好、账号、密钥引用和任务等关键历史状态。就地升级只是必要测试路径,仍需签名、迁移、启动、业务和设备证据。
升级契约应验证数据语义、迁移幂等和中断恢复,而不只检查文件存在。工程判断:保留字节可能因 schema、类型、密钥引用或状态机变化而无法继续使用。具体数据域和恢复策略取决于项目设计,未有项目证据时不能声称迁移通过。

工程常见问题

卸载旧版再安装加固版能算升级测试吗?

不能。卸载会清除关键历史状态,必须从真实旧包准备数据后执行不清数据的覆盖安装,才能验证迁移与身份连续性。

新包能覆盖安装成功是否代表升级兼容?

不代表。安装成功只说明平台身份和版本门槛通过,还要验证数据迁移、新进程初始化、核心业务、异常恢复和设备矩阵。

使用上传密钥签名的本地 APK 能代表 Play 更新吗?

不能。上传密钥与应用签名密钥责任不同,Play 分发路径应使用测试轨道或真实派生安装回执验证最终签名对象。

升级后数据库文件存在是否说明迁移成功?

不能。应通过业务契约检查 schema、关键记录、约束和幂等状态,文件存在但字段语义错误仍属于迁移失败。

需要测试所有历史版本升级吗?

应根据仍受支持版本、真实用户分布、schema 跃迁和高风险变化选择路径,不能只测最近版本,也不必无依据穷举不可达版本。

申请加固升级路径评估前应准备哪些材料?

准备真实旧 APK、加固候选、签名与 split 身份、状态夹具、迁移契约、首次启动、核心路径、退出记录和设备矩阵,再从御盾中央平台提交申请。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: App 加固 PoC 如何形成发布结论