灌糖包子 c5ef726134
移除验证码解析功能,简化为纯短信转发
- 删除 VerificationCodeParser 及相关测试,短信捕获和推送不再解析验证码
- 飞书推送改为只发送短信原文,时间戳格式化为可读日期
- 移除主界面"只推送验证码"开关和"调试时上传完整短信正文"选项
- 移除"保存轮询间隔"按钮,开启轮询时自动保存间隔(未输入默认1秒)
- 按钮文字从"开始1秒轮询验证码"改为"开始短信轮询"
- 删除"打印最近30条短信"功能及相关 SmsInboxReader.logRecentMessages
- SmsInboxReader 用 RecentSmsResult 替换 RecentCodeResult
- FeishuWebhookConfigStore.Config 移除 filterVerificationCode/sendFullBodyDebug
- 修复代码缩进不一致问题
2026-05-18 22:38:06 +08:00

155 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
当前 `SmsReceive` 目录几乎为空,只有 macOS 生成的 `.DS_Store`,没有 Android 工程和既有 OpenSpec 目录。用户要求先生成完整 spec 方案,再开始编码;同时明确 Android Studio、Gradle、JDK 等环境不在本次方案范围内,后续实现要参考 `Weather reference project` 的构建环境。
目标设备是小米 12S、澎湃 OS 3、Android 15。需求本质是个人自用工具收到手机短信后应用读取短信原文并展示。由于不是 Play Store 上架应用,方案可以直接使用短信权限,但仍要面对 Android 运行时权限、Android 15 受限权限策略、厂商后台管理和短信广播分发行为。
官方 API 判断如下:
- Android `Telephony.Sms.Intents.SMS_RECEIVED_ACTION` 是收到文本短信的系统广播,需要 `RECEIVE_SMS` 权限。它是读取任意短信原文最直接的路径。
- Google `SMS Retriever API` 不需要 `READ_SMS``RECEIVE_SMS`,但短信必须包含 app hash适合服务端短信模板可控的场景不适合读取所有第三方短信。
- Google `SMS User Consent API` 可以请求用户授权读取单条短信,不要求 app hash但需要弹出用户确认适合作为受限权限或广播异常时的对比验证路径。
## Goals / Non-Goals
**Goals:**
- 先形成可执行 spec不直接写业务代码。
- 建立 Android 15 上读取短信原文的主路径和备选路径。
- 明确短信原文展示、权限状态、诊断状态和真机验证标准。
- 后续实现应保持最小化:一个主界面、一个接收链路、一组诊断信息,不做复杂产品化。
- 保持短信内容本地处理,默认展示短信原文、来源、时间和短诊断摘要。
**Non-Goals:**
- 不做完整短信客户端,不替代系统短信 App。
- 不实现发送短信、删除短信、读取历史短信库或同步短信到云端。
- 不处理 Android Studio、Gradle、JDK 的重新安装和环境拉取。
- 不以 Google Play 上架合规作为约束目标。
- 不保证所有银行、平台、运营商短信都能被无条件读取;必须通过真机验证确认。
## Decisions
### Decision 1: 主路径使用 `SMS_RECEIVED_ACTION` + `RECEIVE_SMS`
主路径选择系统短信广播。原因是目标是读取“我自己的手机收到的短信原文”,短信来源不可控,很多短信不会带当前 app 的 hash`SMS Retriever API` 无法覆盖任意短信。系统广播能拿到完整 PDU再通过 `Telephony.Sms.Intents.getMessagesFromIntent(Intent)` 合并为正文,是最符合目标的能力。
实现要求:
- Manifest 声明 `android.permission.RECEIVE_SMS`
- 对 Android 6.0+ 执行运行时权限申请。
- 注册接收 `android.provider.Telephony.SMS_RECEIVED` 的 receiver。
- receiver 内只做轻量解析和状态分发,避免长耗时。
- 记录最近一次接收时间、sender、body 摘要、读取结果和失败原因。
备选方案:
- `READ_SMS` 可读取短信数据库,但需求是监听新短信,不需要读取历史短信;默认不纳入主路径。
- 默认短信应用角色权限更强,但目标不是做短信客户端;不作为一期要求。
### Decision 2: 备选验证路径引入 `SMS User Consent API`
`SMS User Consent API` 用于验证两类问题:
- 当系统广播路径在 HyperOS 上被后台策略影响时,前台触发 consent flow 是否能拿到单条短信。
- 当用户不愿或系统不允许直接授予短信权限时,是否仍能通过一次性确认读取短信原文。
限制:
- 它不是静默读取,需要用户确认。
- 它适合前台验证流程,不适合后台长期监听所有短信。
- 它依赖 Google Play services国内 ROM 环境下需要确认设备实际可用性。
### Decision 3: `SMS Retriever API` 只作为受控短信模板能力
`SMS Retriever API` 的优点是无需短信权限,体验干净;但它要求短信包含 app hash且通常需要服务端发送符合格式的短信。对于读取第三方平台短信它大概率不适用。因此一期只实现或预留为“自发测试短信/未来自控服务端短信”的能力,不作为读取任意短信的主线。
### Decision 4: 最近结果直接以短信原文为主
当前目标不再是从短信中提取结构化业务字段,而是直接保留收到的短信原文。因此最近结果与诊断数据应围绕“是否成功读取到完整短信正文”展开,而不是围绕解析命中策略。
建议顺序:
1. 优先保证 PDU 合并后的短信正文完整。
2. 记录 sender、timestamp、source 和 body 摘要。
3. 读取失败时保留失败原因,例如无权限、未收到广播、正文为空。
4. 默认展示最近一条短信原文,不额外推断正文中的业务字段。
### Decision 5: UI 首版只做诊断型工具界面
首版 UI 应该服务验证,而不是做复杂产品。建议显示:
- 当前短信权限状态。
- 主路径 receiver 状态。
- Google Play services / SMS User Consent 可用性。
- 最近一次收到短信的时间、发送方、来源和短信原文。
- 最近失败原因例如无权限、未收到广播、正文为空、API timeout。
- 手动清空最近结果按钮。
### Decision 6: 本地隐私边界
即使是自用 app也应尽量控制短信内容扩散。建议
- 默认只持久化最近一条短信原文、时间、sender 摘要和读取状态。
- 核心能力不以网络上传为前提。
- 日志避免输出完整短信正文debug 模式如需输出,必须集中开关控制。
## Android 15 And HyperOS Risk Analysis
- [Risk] `RECEIVE_SMS` 在 Android 15 或厂商系统上被标记为高风险/受限权限,安装来源和系统设置可能影响授权。
→ Mitigation: 首次启动展示权限状态;如果权限申请失败,引导到应用详情页检查“受限权限/权限管理”;同时使用 consent API 做对比验证。
- [Risk] 后台接收被 HyperOS 省电策略限制。
→ Mitigation: 首轮验证覆盖前台、后台、锁屏三种状态;如后台不稳定,增加前台服务或引导关闭省电限制作为后续任务。
- [Risk] Google Play services 在目标设备上不可用或版本不满足。
→ Mitigation: Google API 作为备选路径,主路径不依赖它;诊断页显示可用性。
- [Risk] 双卡、国际短信、长短信 PDU 合并导致 sender 或正文异常。
→ Mitigation: 使用官方 `getMessagesFromIntent` 解析,按 message body 拼接,记录 subscription id 如可用。
- [Risk] 正则误识别。
→ Mitigation: 解析结果带命中策略和置信度;测试用例覆盖误判样本。
## Migration Plan
本项目当前没有既有代码,迁移计划等同于实施顺序:
1. 完成本次 OpenSpec 评审。
2. 参考 Weather 项目创建或复制最小 Android 构建骨架。
3. 实现权限和诊断 UI。
4. 实现系统短信广播主路径。
5. 实现短信读取结果存储和展示逻辑。
6. 在小米 12S 上跑真机验证。
7. 根据真机结果决定是否补 `SMS User Consent API` 或后台稳定性处理。
Rollback 策略:
- 如果系统广播路径被目标设备限制,保留解析器和 UI降级为 `SMS User Consent API` 前台读取验证。
- 如果 Google API 不可用,不影响系统广播主路径。
## Validation Strategy
上下文验证:
- 确认 Weather 项目可作为构建环境参考。
- 确认 `SmsReceive` OpenSpec 通过 CLI validate。
- 确认 spec 任务列表不包含重装 Android Studio、Gradle、JDK。
代码验证:
- 短信读取与结果存储测试覆盖多段短信、正文为空、权限缺失和最近结果刷新样本。
- receiver 解析逻辑可通过构造 Intent/PDU 或抽象 message input 测试核心逻辑。
- 权限状态和诊断状态可通过 ViewModel/unit test 验证。
真机验证:
- 前台打开应用后,向目标号码发送测试短信:`【测试】这是一条短信原文样本。`
- 应用退到后台后,重复发送不同短信正文。
- 锁屏状态下发送短信,解锁后检查最近结果。
- 若可以控制短信格式,发送带 app hash 的 SMS Retriever 测试短信。
- 若广播路径失败,打开前台 consent flow 再发送短信,观察是否弹出授权并读取正文。
## Open Questions
- 目标小米 12S 当前是否安装并启用了 Google Play services。
- 用户是否接受为了后台稳定性关闭 HyperOS 对该 app 的省电限制。
- 首版是否需要常驻通知显示最近短信结果,还是只在 app 内展示。
- 是否需要支持短信原文快捷复制;这会带来额外隐私和系统提示问题,建议先不做。