12 KiB
12 KiB
Android SDK 重构实施接管文档
更新时间:2026-07-26 分支:
main
发布约束:Maven 制品只允许通过https://jenkins.xuqinmin.com/发布。
当前目标
sdk-core是配置、会话、网络、文件、时间等公共能力的唯一实现。sdk-update只提供更新领域 API;宿主与 RN 桥接不复制下载、校验和安装代码。- APK 更新必须支持 SHA-256、原子临时文件、进度、取消、有限重试、FileProvider、未知来源安装权限及确定错误码。
2026-07-27 sdk-file 与宿主 FileProvider 隔离
sdk-file不再直接把androidx.core.content.FileProvider注册进宿主 Manifest, 改为注册唯一的XuqmFileProvider子类。- Android Manifest 可能按 Provider 类名合并节点;宿主已有
FileProvider时, 不同 authority 会在合并阶段冲突。 - authority 和公开文件 API 保持不变,宿主不需要
tools:replace或新增 Manifest 配置。
2026-07-17 已完成
FileSDK新增详细下载进度、SHA-256 与哈希匹配公共 API。- 下载改为
.part临时文件完成后替换目标文件;HTTP 非 2xx 失败,协程取消会中断连接并清理临时文件。 - 当时的 core FileProvider 归属已被后续终态替代:当前仅
sdk-file声明,core 零 Manifest。 sdk-update删除无版本 APK 兼容路径和弃用下载入口。sdk-update2.0.0-SNAPSHOT 强制要求合法 SHA-256,并提供下载重试、取消、缓存文件复验和确定错误码。- 增加 Java/RN 可直接使用的
startDownloadAndInstall、UpdateInstallTask、进度/结果回调和安装权限设置 Intent。 sdk-core、sdk-update、sample-app编译通过。- 收敛 update/push/im/sample 的空 package/resource 目录;源码树禁止保留空目录或
.gitkeep占位。 - 清理后
:sdk-core:testDebugUnitTest :sdk-update:testDebugUnitTest重新执行并通过。 sdk-core1 个单元测试、sdk-update2 个单元测试通过。- 修复通知权限检查、MediaStore API 等级和
java.timeminSdk 24 反向移植,core/update Release Lint 均通过。
2026-07-18 RN 宿主网络兼容修复
- App4 真机链路发现
sdk-core:1.1.6-SNAPSHOT将 OkHttp 提升到 5.3.2, 与 React Native 0.86 的okhttp-urlconnection:4.9.2混装后会在首次请求时 因okhttp3.internal.Util缺失导致原生进程崩溃。 sdk-core统一使用 OkHttp BOM 4.12.0;这是 RN 0.86 当前可用的最新 4.x 稳定兼容线,不允许下游单独提升某个 OkHttp 组件到 5.x。check增加verifyNetworkDependencyAlignment,发布前强制确认所有com.squareup.okhttp3组件版本一致。sdk-update的 Gson 改为复用版本目录定义,删除模块内的重复版本事实来源。
后续外部验收
- 从租户平台下载与 Sample 包名匹配的真实
config.xuqmconfig,再执行 Sample Debug/Release 整包和仪器测试。 - 在目标宿主中验证下载取消、错误码、权限跳转和安装器唤起。
- 发布只允许通过 Jenkins 执行;当前开发机未发布任何 Maven 制品。
已验证命令
./gradlew :sdk-core:compileReleaseKotlin :sdk-update:compileReleaseKotlin :sample-app:compileDebugKotlin
./gradlew :sdk-core:testDebugUnitTest :sdk-update:testDebugUnitTest :sample-app:compileDebugKotlin
./gradlew :sdk-core:lintRelease :sdk-update:lintRelease :sdk-update:assembleRelease
不得在开发机执行 Maven 发布。
2026-07-26 初始化、Update 与 BugCollect 收敛
sdk-core新增唯一初始化状态机:IDLE / INITIALIZING / READY / DEGRADED / FAILED, 对外失败使用XuqmSdkException + XuqmErrorCode。- 平台配置以
appKey + packageName + platformUrl隔离,使用加密 LKG 保存七天; 远端配置最多执行三次指数退避重试,有有效 LKG 时降级运行,否则只停用扩展能力。 - 初始化文件唯一位置为
assets/config/config.xuqmconfig。运行时只接受XUQM-CONFIG-V2:Ed25519 签名覆盖前五段 ASCII,先验签、后 AES 解密, canonical JSON、schema、UUID、revision 和 RFC3339 时间均强校验;V1 与未知 keyId 拒绝。 - 已同时内置开发签发公钥
xuqm-config-dev-2026-07与生产签发公钥xuqm-config-prod-2026-07;私钥只保存在 Jenkins 凭据库和服务端运行环境, 不属于 SDK 仓库或发布制品。 BugCollect必须同时满足平台启用、隐私已同意、上传地址有效。关闭时恢复宿主原 UncaughtExceptionHandler 并删除队列;服务端BUGCOLLECT_DISABLED会形成持久停用锁, 仅新的成功平台配置可解除。- error/fatal 加密持久化,最多 500 条且保留七天;普通事件仅内存。后台上传最多 重试三次并加入抖动,不向宿主线程抛错;统一脱敏入口在入队前清理敏感键、手机号、 身份证号和 Bearer Token。
- Update 使用平台动态
updateRequiresLogin:未登录正常跳过;登录后同一会话自动补检 一次;退出或切换用户会取消旧任务并清除灰度检查缓存,回调统一切回主线程。 - BugCollect Gradle 插件拒绝 V1,Release 读取同一 V2 文件;支持
-Pxuqm.bugcollect=disabled应急构建,此时保留本地 mapping 产物但不访问上传服务。 - Android R8
mapping.txt通过POST /bugcollect/v1/artifacts/upload以artifactType=R8_MAPPING上传,并携带 appKey、platform、appVersion、buildId 与文件 SHA-256。它不是 RN Source Map,不改名为.map,也不伪造 module/bundle 身份。 上传使用构建机XUQM_API_TOKENBearer 认证;令牌不进入配置、任务输入快照或日志。 平台启用 BugCollect 但缺少令牌时 Release 明确失败;应急 disabled 构建不要求令牌。 - 所有 Release(无论是否开启混淆)都会在打包前调用
POST /api/sdk/build/config/validate,提交原始签名配置和最终 applicationId。服务不可达、 配置撤销、过期或包名不匹配都会阻止打包;应急关闭 BugCollect 也不能绕过配置校验。 - 删除不再参与 Manifest 的
BugCollectInitProvider,避免扩展模块平行初始化。 - 删除公开手动初始化与旧 Provider 兼容类。
sdk-core单独集成不注册 Provider、 FileProvider 或通知权限;需要平台能力的扩展统一合并同一个XuqmMergedProvider。 FileProvider 归唯一sdk-file,通知权限归sdk-push。 - Sample 删除硬编码 appKey,只接受被 Git 忽略的租户平台签发配置;缺少文件时 Debug/ Release 构建门禁给出明确错误。Release 签名只从用户级 Gradle 属性读取。
- 敏感存储收敛为唯一
SecureStore:Android Keystore 不可导出 AES-256-GCM 密钥, namespace/key 作为 AAD;删除废弃 security-crypto 依赖。 sdk-webview/XWebViewView.kt的既有未提交修改始终作为用户基线保留;本轮只在该基线 上将打开文件能力切换到唯一PlatformFileSDK,未覆盖其它改动。
本轮最终已验证:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process \
:sdk-core:testDebugUnitTest \
:sdk-bugcollect:testDebugUnitTest \
:sdk-update:testDebugUnitTest \
:sdk-bugcollect-plugin:test \
:sdk-core:lintRelease \
:sdk-bugcollect:lintRelease \
:sdk-update:lintRelease
./gradlew --no-daemon \
:sdk-core:assembleRelease \
:sdk-bugcollect:assembleRelease \
:sdk-update:assembleRelease \
:sdk-bugcollect-plugin:assemble \
verifySdkManifestOwnership
./gradlew :sample-app:compileDebugKotlin \
-x :sample-app:validateSampleSdkConfig
./gradlew :sample-app:compileDebugAndroidTestKotlin \
-x :sample-app:validateSampleSdkConfig
Sample 源码编译时只为静态验证跳过真实签发文件的存在性门禁;正常执行
:sample-app:validateSampleSdkConfig 已确认会以明确错误阻止缺配置构建。合并 Manifest
静态结果确认多个扩展最终只有一个初始化 Provider。
尚未执行 Jenkins、Maven 发布、真机仪器测试或带真实租户配置的 Sample 整包。在线校验的不可达、HTTP 失败、
CONFIG_REVOKED、CONFIG_EXPIRED、PACKAGE_MISMATCH 和成功响应已有单元测试覆盖;
服务端真实生成的 V2 固定向量已在 Android 端完成验签、解密和 canonical JSON 精确
比对。R8 multipart 上传字段、Bearer Header、SHA-256、.txt 文件名与令牌不进入请求
正文均有本地 HTTP 契约测试。
2026-07-26 审计后终态修复
- 新增唯一
com.xuqm.common构建插件:扫描宿主src/**,所有变体只接受src/main/assets/config/config.xuqmconfig;改名、嵌套或其它 sourceSet 中出现任何.xuqmconfig都会失败。Debug 执行本地 V2 验签,Release 额外在线校验。buildId 与运行时 BugCollect 构建门禁均归此插件;可选 BugCollect 插件只上传 R8 mapping。 - 解密后的
serverUrl是唯一且必填的平台地址;已删除旧业务默认地址和运行时回退, 配置缺失或地址为空时初始化明确失败。 - 删除
configureServiceEndpoints、useExternalServiceEndpoints、useLocalServiceEndpoints及 Sample 的环境设置页面、路由和持久化。服务端点仅可由 签发配置的serverUrl与该平台远程配置产生;远程字段缺失时只回退到同一serverUrl,不会切换平台。 - Release buildId 优先读取 Jenkins/CLI 的
XUQM_BUILD_ID;本地缺失时使用 UUID。 为防止 Gradle configuration cache 复用随机身份,启用配置缓存的 Release 强制要求显式 buildId。 -Pxuqm.bugcollect=disabled是不可覆盖的构建禁用,同时关闭运行时采集/上传与 mapping 上传;mapping 仍保留为本地产物。Debug 只关闭自动采集,可调试宿主在构建未 禁用、隐私同意、平台启用时可通过隐藏开发入口显式发送一次测试事件。- common 插件按变体生成唯一 Manifest 标记、buildId 与 BugCollect 构建开关;扩展 AAR 的合并 Provider 未检测到该标记时只退出,不执行初始化,core-only 仍保持零 Manifest。
- Update 先服从平台
features.update,相同用户重复同步不会再次补检;下载与安装授权 绑定当前 generation 和 userId,切换或登出会取消活动下载、清除授权并阻止旧灰度结果安装。 - BugCollect 队列 flush 使用 Mutex 单飞,定时与满批触发不会重复上传同一批数据。
- 内部 BugCollect 状态桥接改为私有反射 SPI,不进入 Java/Kotlin 公开 API。
sdk-core保持零 Manifest,只保留无需初始化的纯本地文件能力。新增唯一sdk-file扩展承载租户平台上传与 FileProvider 打开;IM、Update、WebView 复用该扩展。sdk-file的 Uri、File、ByteArray 三个上传入口统一先等待平台初始化,再创建请求体 和读取服务端点;平台失败统一抛出保留code/status/message且不暴露响应体的PlatformFileException。openFile仍为不依赖初始化的纯本地能力。- 全仓日志、标准输出和异常文案不再输出 token、userId、groupId、消息目标、H5
payload、请求/响应正文、文件 Uri/绝对路径等实际值。根任务
verifyNoSensitiveLogging在各模块testDebugUnitTest、lintRelease和check前扫描 Kotlin/Java/Gradle 源码,阻止凭据或用户标识重新进入输出边界。 - 删除文档中不存在的
RetrofitFactory示例;公开文档同步 common/file 的真实边界。
本批最终验证
- core/file/bugcollect/update/im/push 与 BugCollect Gradle 插件单元测试通过;
sdk-common-plugin独立构建测试通过,包含严格data.valid防伪响应和单配置路径测试。 - core/file/bugcollect/update/im/push/webview 的
lintRelease与assembleRelease全部通过。 - Sample 的 Debug Kotlin、AndroidTest 编译以及合并 Manifest 校验通过;因仓库不提交被忽略的
config.xuqmconfig,验证编译时仅显式跳过xuqmValidateConfigDebug。 - 单独执行
xuqmValidateConfigDebug已按预期失败,并明确提示唯一缺失路径src/main/assets/config/config.xuqmconfig;未伪造配置、未绕过在线 Release 校验。 - 普通 Release 生成结果为
buildEnabled=true/automaticEnabled=true;应急参数生成结果为false/false;Debug 为true/false,显式开发测试仍受隐私和平台开关约束。 - Release 未提供
XUQM_BUILD_ID且启用 configuration cache 时按预期失败;提供显式 buildId 后配置缓存可保存、复用,生成 Manifest 中的值保持不变。