先看结论与判断条件
- 面向 Android 16 的加固包应将 targetSdkVersion 设为 36 以适配平台行为变化
- 加固后的 SO 库必须保证所有 LOAD 段的 p_align 为 16384 且文件按 16 KB 对齐,仅运行 zipalign 并不充分
- 兼容框架开关可按 change ID 辅助隔离 Android 16 行为变化,仅用于验证阶段定位问题
- 后台限制的收紧会影响使用前台服务的加固方案,必须验证服务启动权限和可见性策略
- CTS 通过或模拟器验证不能替代多设备、多场景的 instrumented 测试
- 验收需覆盖多厂商设备矩阵,单一设备测试结论不可靠
- 预编译库若未指定 16 KB 页面对齐标志,将在新设备上导致 dlopen 失败
- 非 SDK 接口调用在 API 36 下可能被严格拦截,需替换为公共等价方案
构建元数据扫描与 API 36 验收清单生成
验收 Android 16 加固兼容的第一步是获取并解读构建元数据,而不是直接部署到设备。加固构建系统通常会在最终 APK 中残留 manifest、打包对齐信息和二进制库属性,这些元数据足以判断是否满足 API 36 的关键要求。自动化扫描可以避免人工漏检,同时为持续集成提供阻断条件。本章给出的 Python 脚本会读取 targetSdk、检查 ELF 对齐并输出可审计的 JSON 清单。
脚本通过调用 Android SDK 的 aapt2 工具提取 targetSdkVersion。若该值低于 36,则 Android 16 不会强制启用 targetSdk 关联的行为变化,但这也意味着应用尚未针对新平台正式适配,加固后可能因旧行为在未来版本被移除而产生断层。扫描失败或 aapt2 不可用会立即以非零状态退出,防止因构建工具缺失导致漏检。
ELF 对齐检查部分利用 Python 标准库解析程序头,寻找类型为 LOAD 的段并核对 p_align 是否至少为 16384。同时验证段在 ZIP 内的偏移是否为 16384 的倍数。未对齐的 SO 在 16 KB 页设备上将直接导致 dlopen 失败,引发运行时崩溃。脚本将每个库的检查结果记录进数组,并在任何失败时汇总返回非零退出码,可作为 CI 阻断点。
构建元数据的完整性直接影响后续测试的有效性。如果 APK 签名方案未升级至 V2 或更高,或者 Manifest 中缺少必要的 queries 声明,静态扫描即可提前发现风险。这种前置检查能大幅减少在真机调试阶段的时间消耗,确保进入设备测试环节的包体已具备基本的平台合规性。
| 检查对象 | 验证方法 | 通过标准 | 失败后果 |
|---|---|---|---|
| Target SDK Version | aapt2 dump badging | targetSdkVersion 等于 36 | 无法触发 API 36 特定行为,未来可能失效 |
| ELF LOAD Segment | 解析程序头 p_align 字段 | 所有 PT_LOAD 段对齐值 >= 16384 | 16 KB 页设备 dlopen 失败,应用闪退 |
| SO File Offset | 检查 ZIP 内文件起始偏移 | 偏移量必须是 16384 的倍数 | 内存映射错误,导致完整性校验失败 |
| Signature Scheme | apksigner verify --verbose | 包含 V2 或 V3 签名方案 | Android 16 可能拒绝安装或运行 |
- 确认构建环境中 aapt2 工具可用且版本最新
- 检查所有 .so 文件是否未被压缩且偏移对齐
- 验证 Manifest 中是否声明了必要的 queries 元素
- 确保 CI 流水线在脚本返回非零码时立即终止
#!/usr/bin/env python3
import json, os, struct, sys, zipfile, subprocess
def get_target_sdk(apk_path):
try:
proc = subprocess.run(['aapt2', 'dump', 'badging', apk_path],
capture_output=True, text=True)
if proc.returncode != 0:
raise RuntimeError('aapt2 dump failed')
for line in proc.stdout.splitlines():
if 'targetSdkVersion:' in line:
return int(line.split(":")[1].strip("'"))
except Exception as e:
print(f"Cannot read targetSdk: {e}", file=sys.stderr)
sys.exit(1)
sys.exit(1)
def check_elf_alignment(lib_data, lib_path):
if len(lib_data) < 64 or lib_data[:4] != b'\x7fELF':
return False, 'not a valid ELF'
ei_class = lib_data[4]
is_64 = (ei_class == 2)
if is_64:
e_phoff, e_phentsize, e_phnum = struct.unpack_from('<QHH', lib_data, 32)
else:
e_phoff, e_phentsize, e_phnum = struct.unpack_from('<IHH', lib_data, 28)
for i in range(e_phnum):
offset = e_phoff + i * e_phentsize
if is_64:
p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align = struct.unpack_from('<IIQQQQQQ', lib_data, offset)
else:
p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align = struct.unpack_from('<IIIIIIII', lib_data, offset)
if p_type == 1: # PT_LOAD
if p_align < 16384:
return False, f'LOAD segment alignment {p_align} < 16384'
if p_offset % 16384 != 0:
return False, f'file offset {p_offset} not 16KB-aligned'
return True, ''
def main():
if len(sys.argv) != 2:
print('Usage: scanner.py <apk>', file=sys.stderr)
sys.exit(2)
apk = sys.argv[1]
if not os.path.isfile(apk):
print('APK not found', file=sys.stderr)
sys.exit(1)
result = {'targetSdk': None, 'libs': []}
result['targetSdk'] = get_target_sdk(apk)
fail = False
if result['targetSdk'] != 36:
print(f"Warning: targetSdk {result['targetSdk']} not 36", file=sys.stderr)
with zipfile.ZipFile(apk) as zf:
for name in zf.namelist():
if name.startswith('lib/') and (name.endswith('.so') or '.so.' in name):
elf_data = zf.read(name)
aligned, reason = check_elf_alignment(elf_data, name)
result['libs'].append({'path': name, 'aligned': aligned, 'reason': reason})
if not aligned:
fail = True
print(json.dumps(result, indent=2))
sys.exit(1 if fail else 0)
if __name__ == '__main__':
main()Android 16 targetSdk 36 强制行为变化决策表
当应用的 targetSdk 升至 36 后,Android 16 会激活若干原本以兼容模式保留的行为变更。这些变更涉及前台服务启动限制、权限组可见性、隐式广播接收器、后台位置访问以及非 SDK 接口调用。加固方案如果修改了这些组件的实现(例如将广播注册改为动态代码生成),就可能触发新的运行时异常。因此验收必须针对每个修改点逐一对照官方的行为变更列表,并把相应的破坏性场景纳入测试用例。
行为变更的另一个风险在于默认策略可能与加固的保护逻辑冲突。例如,加固常用基于 AlarmManager 的定时调度或特定权限去执行完整性检查,但 Android 16 对 SCHEDULE_EXACT_ALARM 权限的授予方式进行了收紧,这可能导致定时检查静默失败。验收人员必须确认所有依赖系统调度的加固机制是否在 targetSdk 36 下仍然能按预期获得系统资源。
将每一个行为变化映射到加固模块,并记录其兼容开关 change ID,可以形成可追溯的决策表。这种决策表不仅有助于快速定位回退路径,还能在平台后续版本中反复使用。加固团队应当基于实际使用的加固技术增加细项,而不得假定默认平台行为与旧系统相同。任何未经过验证的假设都可能导致生产环境的安全策略失效。
对于非 SDK 接口的依赖是加固产品的高危区。随着平台迭代,更多内部 API 被列入灰名单或黑名单。在 API 36 下,调用这些受限接口将直接抛出 NoSuchMethodError 或 SecurityException。验收过程中必须使用 veridex 等工具扫描字节码,确保没有遗留的隐藏 API 调用,并将其替换为公开的 AndroidX 或标准库实现。
| 行为类别 | 可能触发的新限制 | 加固相关风险 | 验收检查项 | 兼容开关 Change ID |
|---|---|---|---|---|
| 前台服务与调度 | 必须提供有效前台服务类型并声明相应权限;SCHEDULE_EXACT_ALARM 权限受限 | 加固常依赖前台服务保活或定时任务执行安全检测 | 确认所有 startForeground 调用已通过 Android 16 要求;检查 AlarmManager 调用路径 | 27695893(示例) |
| 权限组可见性 | 受限制的权限组不再对未声明 query 的应用可见 | 加固可能通过权限探测来判断环境,若探测失败可能误判为攻击 | 检查所有 PackageManager 查询权限的代码路径并更新 query 声明 | 27703367 |
| 隐式广播接收器 | 针对部分系统广播的隐式注册被禁止 | 加固的动态注册广播可能无法接收预期的事件,导致安全策略静默失效 | 迁移广播接收器至显式 intent 或使用 JobScheduler 替代 | 27714148 |
| 非 SDK 接口调用 | 更多非公共 API 被 limitlist 拦截 | 加固底层常用 hidden API 或系统内部方法实现保护,调用将抛出 NoSuchMethodError | 删除所有受限灰名单或黑名单接口调用,替换为公共等价方案 | 27753601 |
- 核对加固模块是否依赖任何非 SDK 接口,替换为 AndroidX 或兼容库
- 为所有前台服务声明 foregoundServiceType,并在 manifest 中注册
- 针对 SCHEDULE_EXACT_ALARM 权限检查是否已迁移至 USE_EXACT_ALARM 或用户引导设置
- 确认所有 PackageManager 查询已配置 <queries> 元素
兼容框架开关的选择与隔离策略
Android 16 的兼容框架允许通过 adb 命令或 manifest 中的 compatChanges 属性按 change ID 单独开启或关闭某一行为变更。在加固兼容验收阶段,合理使用这些开关可以快速隔离导致崩溃的行为项,而不必立刻修改加固模块源码。但开关机制无法覆盖全部行为变更,且 Google 明确指出这些开关可能在未来的平台更新中被移除,因此绝不能作为生产环境的永久解决方案。
验收团队应当建立开关隔离测试矩阵:对每个怀疑的行为变更,先在开启和关闭开关的分支上分别运行 instrumented 测试包,观察加固模块的表现差异。如果关闭开关后异常消失,则可以确定根因,并开始永久适配。使用开关时必须记录完整的 device config 上下文,因为部分变更依赖系统版本和补丁级别,开关效果可能不一致。
为了避免开关滥用,建议在 CI 中增加兼容开关强制检查:扫描代码库中的 AndroidManifest.xml 是否包含 compatChanges 属性,并告警提醒将其移除或产出适配任务。只有当相应的加固模块已经过测试且产出了永久适配代码时,才允许合并到主分支。这一实践可防止团队将临时妥协带入生产发布。
从工程实践看,兼容开关适合用来缩小问题范围,不适合长期代替代码修复。加固模块涉及的底层调用和内存行为未必能由一个开关完整还原;定位到具体差异后,应在移除临时开关的条件下复测候选包。
| 使用阶段 | 操作动作 | 预期目的 | 风险与限制 |
|---|---|---|---|
| 开发调试期 | 通过 adb 命令动态切换 change ID | 快速定位导致崩溃的具体行为变更 | 仅限本地调试,不能作为代码提交依据 |
| 测试验证期 | 在 Manifest 中临时声明 compatChanges | 隔离特定问题以验证其他模块功能 | 必须在发布前移除,否则未来版本可能失效 |
| 持续集成 | 扫描 Manifest 是否存在 compatChanges | 防止临时配置被意外合入主分支 | 发现即报错,强制要求提供永久修复方案 |
| 生产发布 | 严禁包含任何兼容开关配置 | 确保应用完全适配当前平台行为 | 依赖开关会导致在后续系统升级中崩溃 |
- 通过 'adb shell dumpsys compatibility' 列出当前设备启用的变更
- 对每个可疑变更运行开启/关闭开关的 instrumented 测试
- 在 CI 中添加 compatChanges 的扫描告警,锁定目标适配时间
- 记录所有使用过的 change ID 及其对应的修复代码提交哈希
16 KB 页面对齐:ELF 与 APK 双重验证
Android 16 在部分设备上强制要求 16 KB 内存页面,这意味着任何通过 mmap 加载的可执行代码段都必须符合 16 KB 的对齐约束。加固产品的 SO 库如果是在旧 NDK 下编译且未指定 16 KB 页面对齐,其 ELF 程序头中的 LOAD 段 p_align 可能仍为 4 KB,这将在 16 KB 设备上造成加载失败。单靠 zipalign 工具对齐 APK 内的 ZIP 条目不足以解决此问题,因为 ELF 页对齐与 ZIP 文件对齐是独立的层次。
验证过程必须包含两个层面:一是 APK 内所有 SO 文件的 ZIP 存储偏移和压缩方式,应确保未压缩且起始偏移为 16384 倍数;二是每个 SO 文件内部的 LOAD 段 p_align 字段和该段文件偏移 p_offset 是否同时满足 16384 倍数的要求。任何一层未对齐都会导致 linker 拒绝加载。市面上的一些加固工具会合并或拆分 SO 文件,合并后的库更需要重新进行对齐验证。
验收人员应使用自动化脚本扫描最终 APK,并结合手动抽查验证。如果加固产品支持多 ABI,需要分别检查每个 ABI 下的所有 SO 文件。任一 ABI 下的任一个 SO 文件未对齐,都应视为验收阻塞项。此外,还需检查预编译库的编译标志,确保构建时使用了正确的链接器参数来强制页面对齐。
对于动态下载的 SO 库,同样适用上述对齐标准。如果应用在运行时从服务器下载保护模块,必须在加载前进行本地校验。未对齐的动态库在 16 KB 页设备上加载时会立即崩溃,且错误日志可能不够直观。因此,下载后的校验步骤应成为加固流程的标准组成部分,防止因网络分发内容不合规导致线上事故。
| 检查项 | 检查方法 | 通过标准 | 失败后果 |
|---|---|---|---|
| ELF LOAD 段 p_align | 使用 readelf -l 或 scanner.py 解析程序头 | 所有 PT_LOAD 段的 p_align >= 16384 | dlopen 失败,引发 UnsatisfiedLinkError |
| ELF 段内文件偏移 | 读取 p_offset,确认其值为 16384 倍数 | 所有 PT_LOAD 段的 p_offset % 16384 == 0 | mmap 返回 EINVAL,进程异常退出 |
| APK 内 SO 文件对齐 | 使用 zipalign -c 4 确认 4 KB,再手动检查 16 KB 对齐;或使用 scanner.py 检查 ZIP 内部偏移 | SO 文件在 APK 内的数据偏移为 16384 倍数且未压缩 | 加载时内存映射错误或性能退化 |
| 预编译 .so 库编译标志 | 检查 NDK 构建脚本是否添加 -Wl,-z,max-page-size=16384 | 使用了 16 KB 页对齐的链接选项 | 未使用正确标志会导致部分段对齐不正确 |
- 确认所有 SO 文件在 APK 中未压缩且偏移对齐
- 验证 ELF 文件头中 PT_LOAD 段的对齐属性
- 检查 NDK 构建脚本中的链接器标志设置
- 对动态下载的库实施相同的对齐校验逻辑
后台限制与前台服务适配要点
Android 16 对后台应用的行为限制继续收紧,targetSdk 36 下的应用若在后台启动前台服务会受到严格审核。加固产品有时会利用前台服务保持与安全服务端的连接,但在新规则下,后台启动前台服务必须符合“短时服务”或“用户可见的紧急类型”等豁免条件,否则会抛出 SecurityException 或服务被系统静默终止。如果加固方案依赖此类机制实现持续保护,很可能在用户退回桌面后彻底失效。
适配的第一步是审计加固代码中所有 Context.startForegroundService() 调用,确认其是否在满足豁免条件的情境下触发。如果某个关键的安全检测服务必须以后台方式运行,应迁移至 WorkManager 或使用随播通知的配套方案。验收测试必须模拟应用从后台返回、屏幕锁定后唤醒等场景,确保安全策略没有中断窗口。
同时需要检查加固服务的可见性策略。如果该服务在 manifest 中设置了 exported=true,Android 16 可能对外部调用施加额外的权限检查。加固产品通常会隐藏此类细节,但兼容验收必须强制暴露并写入 requirements。任何需要跨进程交互的安全逻辑,都应补充显式签名级权限和相应的 <permission> 声明。
对于依赖定时任务的加固逻辑,需特别注意 Doze 模式和 App Standby 的影响。Android 16 进一步优化了电池优化策略,非交互式应用的唤醒频率受到更大约束。验收时应验证在长时间待机后,加固模块能否正常恢复工作,或者是否需要用户交互才能重新激活保护机制。
| 场景类型 | 平台限制 | 加固适配方案 | 验证方法 |
|---|---|---|---|
| 后台启动前台服务 | 需满足短时或紧急类型豁免 | 声明 foregroundServiceType 并确保用户可见 | 模拟后台启动,观察是否抛出 SecurityException |
| 定时任务执行 | Doze 模式限制精确闹钟 | 迁移至 WorkManager 或申请特殊权限 | 长时间待机后检查任务是否按时触发 |
| 跨进程通信 | Exported 组件权限检查加强 | 使用签名级权限保护服务接口 | 尝试从其他应用调用服务,验证权限拦截 |
| 网络保持连接 | 后台网络访问受限 | 使用 Foreground Service 维持连接 | 锁屏状态下监测网络连接存活状态 |
- 审计所有 startForegroundService 调用是否符合豁免条件
- 检查 Manifest 中服务的 exported 属性和权限声明
- 验证 Doze 模式下定时任务的执行情况
- 模拟弱网和断网环境下的服务重连机制
权限与安全变化审查
Google 在 Android 16 中调整了多项权限的授予行为和可见性,例如附近设备、精确位置和通知监听等。如果加固产品在运行时动态请求权限,并依赖授权结果判断设备环境是否正常,新策略可能导致权限请求被自动拒绝或跳转至系统设置,从而破坏加固的决策树。验收时应逐项对照目标行为变更列表中与权限相关的条目,并在测试中使用“权限拒绝一次”“始终拒绝”等用户选择路径。
另一个热点是 APK 签名方案的强制升级。来自 Android 16 的面向所有应用的行为变更可能要求应用必须使用 V2 或更高签名方案,旧版签名的加固包可能在安装时被拒绝。加固过程中的二次打包步骤通常会重新签名,如果仍然沿用 V1 签名方案,就会在 Android 16 上无法安装。验收必须确认发布包同时包含 V2 或 V3 签名,且签名密钥满足平台信任链要求。
对于使用密钥库或 Android Keystore 实现数据保护的加固模块,还需要关注密钥算法约束。虽然平台文档未明确列出所有禁用算法,但工程经验表明弱密钥算法可能被禁用,导致 KeyGenParameterSpec 失败。应在设备端自动化测试中对所有密钥生成、签名和加解密操作进行回归,确保在最新安全补丁下仍能正常工作。
权限请求的时序也是验收重点。Android 16 可能对频繁请求敏感权限的行为进行限制,甚至暂时冻结应用的权限请求能力。加固产品在进行环境检测时,应避免在短时间内重复请求同一权限,而是采用缓存策略或引导用户手动设置,以防止触发系统的防骚扰机制。
| 检查维度 | 潜在风险 | 合规要求 | 测试手段 |
|---|---|---|---|
| 动态权限请求 | 请求被自动拒绝或跳转设置 | 遵循最小权限原则,处理拒绝回调 | 模拟用户拒绝权限,验证应用降级逻辑 |
| APK 签名方案 | V1 签名被拒绝安装 | 必须使用 V2 或 V3 签名方案 | 使用 apksigner 验证签名方案版本 |
| 密钥生成算法 | 弱算法被禁用导致异常 | 使用推荐的强算法和密钥长度 | 在真机上执行密钥生成和加解密测试 |
| 权限请求频率 | 触发系统防骚扰限制 | 增加请求间隔,缓存用户选择 | 高频次请求测试,观察系统反应 |
- 验证所有动态权限请求的处理逻辑是否健壮
- 确认 APK 签名方案包含 V2 或 V3
- 测试密钥生成和加解密操作在最新系统上的表现
- 检查权限请求频率控制策略是否生效
多 ABI 与预编译库的兼容性要求
现代加固工具通常集成预编译的 .so 文件来执行反调试、完整性校验等操作。这些预编译库可能覆盖 armeabi-v7a、arm64-v8a 和 x86_64 等多种 ABI。Android 16 在不同 ABI 上对 16 KB 页面的支持策略并非完全相同,而且部分厂商可能仅在 64 位进程上启用 16 KB 页。因此,不能因为 arm64-v8a 对齐通过就认为整个加固包安全。
验收必须为每个 ABI 独立生成报告。如果加固产品声明仅支持 64 位,则需确保所有 32 位目录已从 APK 中剔除,否则安装器会因缺失 32 位库而拒绝在 32 位设备上安装,而不是优雅降级。此外,如果使用 App Bundle 分发,需检查生成的 standalones APK 是否含有正确的 ABI 伴随对齐。
加固团队在集成第三方库时,应保留编译链信息以便快速回溯对齐失败的原因。建议在 CI 中为每个 ABI 构建单独的测试任务,并行运行 scanner.py,并将失败项关联到具体 ABI 和库版本。对于动态加载的库,也需确保其编译时指定了正确的页面对齐参数,避免运行时出现不一致。
针对不同 ABI 的测试设备选择也至关重要。x86_64 架构的模拟器通常用于快速验证,但不能完全代表 ARM 真机的行为。特别是涉及到硬件辅助虚拟化或特定指令集优化的加固逻辑,必须在真实的 ARM 设备上进行验证,以确保在所有目标设备上都能正常运行。
| 检查维度 | 检查方法 | 关键准则 | 常见失败模式 |
|---|---|---|---|
| ABI 完整性 | 检查 APK lib 下的目录是否与构建目标一致 | 无缺漏 ABI,也无多余未对齐的 ABI 目录 | arm64 设备因缺少 64 位库而使用 32 位路径,但 32 位库未对齐崩溃 |
| 单一库对齐交叉验证 | 分别对每个 ABI 子目录运行 scanner.py | 该 ABI 内所有 SO 对齐通过 | 仅有 arm64 对齐,x86_64 未对齐导致模拟器测试失败 |
| App Bundle 拆包对齐 | 使用 bundletool build-apks 并抽查各 ABI APK 的对齐 | 每个独立 APK 内 SO 对齐符合 16 KB 要求 | 由于 bundle 配置不当,拆出的 APK 仍使用 4 KB 对齐 |
| 预编译库编译链审查 | 提取 .so 的 .comment 或 .note 段,确认 NDK 版本 | 使用了支持 16 KB 页对齐的 r23+ 版本并设置了正确链接标志 | 旧 NDK 生成库未设置 max-page-size,导致 p_align 仍为 0x1000 |
- 确认 APK 中只包含目标 ABI 的库文件
- 对每个 ABI 目录下的 SO 文件进行对齐扫描
- 验证 App Bundle 拆分后的 APK 对齐情况
- 检查预编译库的 NDK 版本和编译标志
设备端 instrumented 测试矩阵与验收策略
静态扫描可以发现对齐和清单问题,模拟器适合快速跑通主路径,但两者都不能代替 ARM 真机上的应用回归。设备侧测试应覆盖目标厂商、页大小和关键业务入口,并把失败日志关联到同一候选包。
测试矩阵需覆盖不同系统版本、安全补丁级别和硬件内核配置(如页大小)。如果加固方案声称支持 16 KB 页,还需专门采购或借测已知支持 16 KB 页的设备。测试脚本应模拟真实用户流程,包括登录、支付、文件浏览等会触发加固安全回调的场景,并利用 AndroidX Test 框架捕获 UnsatisfiedLinkError、SecurityException 和 ANR。
验收策略应包含回归测试计划和失败上报机制。一旦发现加固模块在特定设备上失败,需要及时记录变更 ID、设备信息、logcat 和 tombstone。这些证据将用于区分是平台行为变更、对齐问题还是厂商定制化引入的缺陷。最终验收报告应列出通过设备列表和已知限制,明确加固的兼容边界。
最终报告分别列出静态检查、模拟器冒烟与真机回归结果,不把其中任何一项写成完整兼容证明。涉及底层内存和信号处理的路径,以目标硬件上的实际日志作为结论依据;尚未覆盖的设备和 ABI 继续标为限制。
| 测试类型 | 覆盖目标 | 所需设备 | 自动化工具 | 注意事项 |
|---|---|---|---|---|
| 强制行为回归 | targetSdk 36 下所有变更点是否导致崩溃 | 多厂商 Android 16 设备 | AndroidX Test + UIAutomator | 对每个行为变更准备独立的 instrumentation 用例 |
| 16 KB 页对齐验证 | 所有 SO 加载成功且无 PageSize 错误 | 已知 16 KB 页设备 | 自定义 JUnit 调用 System.loadLibrary 并监听异常 | 若没有真机,可暂时使用 Android Emulator 配置 16 KB 页内核启动参数,但不可作为最终通过依据 |
| 后台限制与服务保活 | 前台服务启动、定时任务触发 | 商用 Android 16 设备,屏幕关闭场景 | Monkey + adb 命令 | 需要模拟 doze 和 app standby 状态 |
| 权限与签名兼容 | 权限请求流、安装器验证 | 同行为回归设备 | Espresso + GrantPermissionRule | 必须使用签名后的加固包,不能使用 debug 签名 |
- 准备至少两部不同厂商的 Android 16 测试设备
- 编写覆盖核心业务流程的 instrumented 测试用例
- 配置自动化测试框架以捕获异常和崩溃日志
- 建立失败用例的分析和上报流程
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| targetSdk 36 导致大屏、权限、调度和安全等平台行为发生强制改变 | Android 16 target behavior changes | 列表会随平台文档更新,必须按实际 targetSdk 和功能筛选。 |
| 部分 Android 16 行为变化不依赖 targetSdk,旧目标版本也需回归 | Android 16 all-app behavior changes | 平台变化不意味着所有加固应用都会触发同一问题。 |
| 兼容框架 change ID 可辅助隔离 Android 16 行为变化 | Android 16 compatibility framework | compat flag 只用于定位,不应替代最终目标配置的验收。 |
| 16 KB 页面对齐需要同时核对 ELF LOAD 对齐、APK 对齐和预编译库 | Support 16 KB page sizes | 只检查 zipalign 或单一 ABI 都不足以得出兼容结论。 |
| CTS 验证设备与 Android 兼容性定义的一致性 | Android CTS overview | 设备通过 CTS 不代表第三方 App 加固后的业务兼容通过。 |
| 真实运行时行为必须通过设备端 instrumented test 验证 | Android instrumented tests | 单一设备通过不能代表完整 API、ABI 和厂商矩阵。 |
| Android 16 对前台服务启动施加了额外的限制,targetSdk 36 可能需要声明新的权限或使用特定启动类型 | Android 16 target behavior changes | 此变化仅当应用实际使用前台服务时影响验收。 |
| 部分与 ART 相关的行为变更(如 JIT 策略)可能对所有应用生效,影响加固后代码的运行时表现 | Android 16 all-app behavior changes | 只提及行为变更,不代表具体 ART 优化实现细节。 |
工程常见问题
加固应用设置 targetSdk 36 后最应该关注哪项变化?
必须逐一核对 Android 16 目标行为变更列表中的强制项,特别是与前台服务、广播接收器限制和权限相关的条目。加固方案如果修改了这些组件的实现,需要额外回归。兼容框架开关可以帮助隔离,但不能作为正式方案。
如何快速检查 APK 中所有 SO 文件的 16 KB 页面对齐?
运行文章代码块中的只读扫描脚本,它会校验 ELF LOAD 段的 p_align 以及文件偏移是否 16384 对齐,并报告不满足条件的库。
兼容框架开关是否可以在生产版本中永久关闭某个行为变化?
不可以。兼容框架开关的目的是帮助开发者在过渡期验证应用兼容性,Google 可能在未来版本移除开关,因此最终必须适配平台行为。
仅通过模拟器或 CTS 测试能否保证加固兼容性?
不能。模拟器通常使用 4 KB 页,无法覆盖 16 KB 设备;CTS 只验证平台一致性,不包含加固后的业务负载。必须使用真实设备运行 instrumented 测试。
如果加固方案只支持 arm64-v8a 一种 ABI,还需要检查什么?
即使单一 ABI,仍需验证该 ABI 下所有 SO 的页面对齐、链接器兼容性以及在不满足 16 KB 的旧设备上的回退行为。此外,Android 16 可能强制 16 KB 对齐,未对齐将直接崩溃。
加固验收清单的输出格式和退出码代表什么?
脚本输出 JSON 结构,每项包含检查项名称、是否通过和失败原因。退出码非零表示至少一项关键检查未通过,可持续集成流水线应据此阻断发布。