[update] init

This commit is contained in:
邹超
2026-05-18 11:10:52 +08:00
commit 790afd679e
54 changed files with 5015 additions and 0 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-17
@@ -0,0 +1,203 @@
## Context
当前 app 已有短信验证码接收实现:Manifest 声明了 `RECEIVE_SMS``READ_SMS`,静态 `SmsReceiver` 监听 `android.provider.Telephony.SMS_RECEIVED``MainActivity` 提供权限申请、最近结果展示、`READ_SMS` 最新短信读取、ContentObserver 和短时轮询诊断。用户反馈“逻辑试过了能用”,但不确定为什么某些场景 `onReceive` 收不到。
目标设备是小米 12S、澎湃 OS 3、Android 15。根据 Android 官方文档,`RECEIVE_SMS` 允许应用接收 SMS,但它是 dangerous 且 hard restricted 权限,是否能真正持有可能受安装来源/安装器 allowlist 影响。Android 15 又新增了 `BOOT_COMPLETED` 启动部分前台服务类型的限制;Doze/App Standby 白名单也不是无限制后台执行。小米官方支持文档说明 HyperOS/小米系统存在“Background autostart”用户开关,路径为 Settings > Apps > Permissions > Background autostart。
参考资料:
- Android 15 前台服务类型变更:https://developer.android.com/about/versions/15/changes/foreground-service-types
- Android 15 行为变更:https://developer.android.com/about/versions/15/behavior-changes-15
- Doze/App Standby 与电池优化:https://developer.android.com/training/monitoring-device-state/doze-standby
- `RECEIVE_SMS` 权限:https://developer.android.com/reference/android/Manifest.permission#RECEIVE_SMS
- 小米后台自启动设置:https://www.mi.com/global/support/faq/details/KA-507608/
## Goals / Non-Goals
**Goals:**
- 先形成完整 spec,不直接写代码。
- 在当前可用短信接收 app 上补齐后台保活和开机恢复方案。
- 让用户能手动完成小米设置,并在 app 内看到哪些设置已完成、哪些只能人工确认。
-`onReceive` 收不到时有明确诊断:权限、安装来源、force-stop、系统广播、HyperOS 后台策略、短信是否进入收件箱。
- 后续实现保持可解释、可关闭、可验证。
**Non-Goals:**
- 不做无通知、不可感知、规避系统策略的隐藏保活。
- 不承诺杀进程、force-stop、清后台、系统级省电清理后仍 100% 存活。
- 不把 C++ fork/native daemon 当成可靠能力;Android 应用沙箱和系统进程管理不会因为 native 代码而失效。
- 不把 app 做成默认短信客户端。
- 不要求本轮编译。
## API Strategy
### 1. 系统短信广播仍是主路径
短信进入设备时,主路径仍是 `SMS_RECEIVED_ACTION`。这是最直接的验证码捕获路径,但它依赖:
- 应用真正持有 `RECEIVE_SMS`
- 应用未被用户 force-stop。
- 系统或厂商策略允许静态 receiver 在当前状态下被拉起。
- 短信确实由 Android Telephony 入库/分发,而不是被系统短信 app 或安全策略特殊处理。
现有 `SmsReceiver` 的 manifest 写法包含 `android:permission="android.permission.BROADCAST_SMS"`,该写法用于限制只有持有系统广播权限的发送方能投递该 receiver;系统短信广播通常满足此条件。后续诊断要验证它不是问题根因,但不建议先移除。
### 2. 前台服务用于“可见后台运行”,不用于读取短信
新增 `SmsKeepAliveService`
- app 前台点击“开启常驻保活”后调用 `startForegroundService`
- 服务在 5 秒内调用 `startForeground`,展示低打扰常驻通知。
- 通知内容显示“短信监听运行中”、最近一次心跳、最近一次短信来源。
- 服务只做状态心跳、通知刷新、诊断状态保存。
- 短信接收仍由 `SmsReceiver` 和收件箱兜底路径处理。
这样做的原因是前台服务可以提升后台进程可见性,但不能替代短信广播,也不能保证被厂商永不杀。它的价值是让系统和用户明确知道该 app 在后台运行,并给 HyperOS “省电无限制 + 自启动”一个更稳定的承载对象。
### 3. 开机自启动采用轻量 BootReceiver
新增 `BootReceiver` 监听:
- `android.intent.action.BOOT_COMPLETED`
- `android.intent.action.LOCKED_BOOT_COMPLETED`
- `android.intent.action.MY_PACKAGE_REPLACED`
收到后:
- 记录 boot 事件和时间。
- 检查用户是否曾开启“常驻保活”。
- 如果已开启,则尝试启动 `SmsKeepAliveService`
- 捕获 `ForegroundServiceStartNotAllowedException` 等异常,写入诊断,不崩溃。
Android 15 对 `BOOT_COMPLETED` 启动前台服务的限制集中在 dataSync、camera、mediaPlayback、phoneCall、mediaProjection、microphone 等类型。当前保活服务不应声明这些类型;如果后续升级 targetSdk 到 35,仍必须真机验证 BootReceiver 启动服务是否被 HyperOS 额外限制。
### 4. 电池优化与 HyperOS 设置采用“检测 + 引导”
Android 标准能力:
-`PowerManager.isIgnoringBatteryOptimizations(packageName)` 展示是否在电池优化白名单。
- 提供 `Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS` 打开白名单设置。
- 个人自用场景可考虑 `ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` 直接请求,但 UI 必须说明它仍不等于无限后台。
小米/HyperOS 能力:
- 提供“打开小米自启动设置”按钮,优先尝试常见 MIUI/HyperOS 安全中心组件。
- 如果显式组件不可用,回退 `ACTION_APPLICATION_DETAILS_SETTINGS` 和通用系统设置。
- UI 显示人工清单:
- 权限:短信权限已授权。
- 自启动:用户已在 HyperOS 开启。
- 省电策略:用户已设为无限制。
- 通知:常驻通知未被关闭。
- 保活服务:正在运行。
自启动和省电无限制通常没有稳定公开 API 可直接读取真实状态,因此必须允许用户手动勾选“我已完成设置”,并把这部分标记为人工确认。
### 5. Native/C++ 方案只做实验诊断
可以加入一个可选 native heartbeat
- Java 服务加载 native library。
- native 层定期写入一个本地 heartbeat 文件或通过 JNI 回调返回时间戳。
- 用于确认进程活着、JNI 正常、服务被杀后心跳停止。
不建议实现 native fork daemon
- Android 应用进程被系统管理,子进程同属应用 UID/cgroup,厂商清理通常会一起处理。
- 后台私自 fork 常驻进程会带来电量、兼容性和安全风险。
- 对短信广播恢复没有直接帮助。
如果用户坚持尝试,必须放在“实验开关”下,并以诊断结论交付,不作为保活主链路。
## Why `onReceive` May Not Fire
后续 UI 和 logcat 诊断必须覆盖以下根因:
- `RECEIVE_SMS` 未授权,或因 hard restricted 机制安装后实际不可持有。
- app 被 force-stopAndroid 不会为 force-stopped app 投递大多数隐式广播,直到用户再次打开。
- 小米自启动未开启,系统不允许后台拉起静态 receiver 或服务。
- 省电策略不是无限制,后台/锁屏/待机时进程或广播处理被延迟/限制。
- app 刚安装后从未启动,部分系统不会让 receiver 正常进入用户期望状态。
- 短信由运营商/RCS/系统短信能力特殊处理,未走普通 SMS_RECEIVED。
- 双卡或短信格式异常导致 PDU 解析失败,但此时 receiver 应该已有日志。
- Manifest receiver 被禁用、包名不匹配、构建安装的不是当前调试版本。
- 通知权限关闭不应直接阻止短信广播,但可能影响前台服务可见性和用户判断。
诊断顺序建议:
1. 看 UI 权限状态和最近 boot/service 心跳。
2. 发送短信时抓 logcat `SmsReceive`
3. 如果 receiver 无日志,点“读取最新短信”确认短信是否入库。
4. 如果短信已入库但 receiver 无日志,重点排查权限、force-stop、HyperOS 自启动和省电。
5. 如果 receiver 有日志但无验证码,排查 PDU/body/parser。
## Data Model
新增 `KeepAliveState` 本地状态:
- `enabledByUser`: 用户是否开启常驻保活。
- `serviceRunning`: 最近一次服务 onCreate/onStartCommand/onDestroy 状态。
- `lastHeartbeatMillis`: 服务最近心跳。
- `lastBootEvent`: 最近一次 boot/package replaced 事件。
- `lastServiceStartFailure`: 最近一次服务启动失败原因。
- `batteryOptimizationIgnored`: 标准 Android 电池优化白名单状态。
- `manualAutostartConfirmed`: 用户手动确认已开启小米自启动。
- `manualBatteryUnrestrictedConfirmed`: 用户手动确认已设置省电无限制。
- `notificationEnabledHint`: 通知是否可能可见。
保存方式优先使用 `SharedPreferences`,与现有 `SmsCaptureStore` 保持简单一致。
## UI Strategy
首版继续使用现有 Java View UI,不引入新框架:
- 增加“后台保活状态”区域:
- 保活开关。
- 服务运行状态。
- 最近心跳。
- 最近开机事件。
- 最近启动失败原因。
- 增加“系统设置”区域:
- 打开应用详情。
- 打开电池优化设置。
- 请求忽略电池优化。
- 打开小米自启动设置。
- 人工确认自启动已开启。
- 人工确认省电无限制已开启。
- 增加“短信广播诊断”区域:
- 最近 `system_sms_broadcast` 时间。
- 最近 `sms_inbox_*` 时间。
- 当收件箱有新短信但广播未到时提示“疑似广播未投递”。
## Test Strategy
单元/本地测试:
- `KeepAliveStateStore` 读写测试。
- `BootReceiver` 在不同 action 下生成正确状态。
- `SmsKeepAliveService` 的状态更新逻辑抽出为纯 Java 方法测试。
- 设置 intent builder 测试:小米组件不可用时能 fallback。
ADB/真机验证:
- `adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.smsreceive.app/.BootReceiver`
- 重启手机后验证保活服务是否恢复。
- 开启保活通知后,后台 30 分钟、锁屏 30 分钟分别发送短信。
- 开启/关闭小米自启动、开启/关闭省电无限制,对比短信广播和收件箱兜底结果。
- 手动 force-stop 后发送短信,确认不承诺接收,并在再次打开 app 后展示诊断。
- Android Doze 测试可参考官方命令 `dumpsys deviceidle force-idle``am set-inactive`
## Rollout Plan
1. 完成本 OpenSpec 并 validate。
2. 先实现 Java 层 BootReceiver、Foreground Service、通知和状态 UI。
3. 再补小米设置入口和人工确认状态。
4. 最后根据真机结果决定是否加入 native heartbeat 实验。
5. 如果前台服务/开机恢复在 HyperOS 上仍不稳定,交付时明确记录限制和必须手动设置项。
## Open Questions
- 当前安装方式是 Android Studio/adb 直装,还是包管理器/文件管理器安装 APK;这会影响 hard restricted SMS 权限表现。
- 用户是否接受常驻通知一直显示。
- 是否希望 app 开机后自动开启保活,还是必须用户先在 UI 中开启一次后才持久化。
- 是否需要 native heartbeat 实验;建议第一轮 Java 方案真机验证后再决定。
@@ -0,0 +1,70 @@
## Why
当前 `SmsReceive` 已经能在部分场景接收并解析验证码短信,但目标设备是小米 12S、澎湃 OS 3、Android 15,后台策略比标准 Android 更激进。仅靠 `SMS_RECEIVED_ACTION` 静态广播不够,需要补齐“开机后自动恢复监听、后台运行可见、系统设置可引导、失败可诊断”的完整方案。
这次需求的重点不是规避系统限制,而是在个人自用 sideload/debug app 的边界内,把 Android 官方后台机制、HyperOS 人工设置、短信广播兜底路径和可验证诊断组合起来。用户也会手动在小米设置中开启“自启动”和“省电策略-无限制”,因此方案应显式利用这个前提。
## What Changes
- 新增小米/Android 15 后台保活规格,覆盖:
- 开机自启动:注册 `BOOT_COMPLETED``LOCKED_BOOT_COMPLETED``MY_PACKAGE_REPLACED`,开机后恢复轻量状态并尝试启动保活服务。
- 通知栏常驻:增加前台服务 + 低打扰常驻通知,用于让进程在后台更可见、更不容易被系统回收。
- 短信广播链路:保留当前 `RECEIVE_SMS` + `SMS_RECEIVED_ACTION` 主路径,增加更清晰的“为什么 onReceive 收不到”的诊断。
- 服务链路:增加 `SmsKeepAliveService`,只负责常驻通知、状态心跳和轻量诊断,不在服务内做耗时短信扫描。
- 收件箱兜底:保留 `READ_SMS` 最新短信读取、ContentObserver、手动读取和短时轮询,用于确认短信已入库但广播未到达的场景。
- HyperOS 设置引导:提供打开应用详情、忽略电池优化设置、小米后台自启动设置的入口,并在 UI 展示用户需要手动完成的清单。
- Native/C++ 尝试边界:允许加入 NDK native heartbeat 作为实验诊断,但不把 C++ fork/daemon 作为可靠保活主方案。
- 明确 Android 15 限制:
- `RECEIVE_SMS` 是 dangerous 且 hard restricted 权限,安装来源/安装器 allowlist 可能影响授权。
- Android 15 对 `BOOT_COMPLETED` 启动部分类型前台服务有限制,不能把 dataSync/media 等类型当成开机拉起通道。
- Doze/App Standby 即使加入电池优化白名单也仍会有部分限制,必须用真机验证而不是只看 API 成功。
- 不直接修改业务代码。后续实现前需要先用 OpenSpec 校验本 change。
## Capabilities
### New Capabilities
- `sms-background-keepalive`: 定义开机广播、前台服务、常驻通知、保活心跳和失败恢复行为。
- `xiaomi-hyperos-background-setup`: 定义小米/HyperOS 自启动、省电无限制、电池优化、权限设置入口和用户操作清单。
- `sms-receiver-delivery-diagnostics`: 定义 `onReceive` 不触发时的排查维度,包括权限、安装来源、force-stop、开机广播、厂商后台策略、短信是否入库、广播是否被系统限制。
### Modified Capabilities
- `sms-code-capture`: 后续实现应把短信广播主路径和收件箱兜底路径接入统一诊断来源,明确区分 `system_sms_broadcast``sms_inbox_observer``sms_inbox_manual``sms_inbox_polling``boot_keepalive`
- `sms-permission-diagnostics`: 后续实现应补充电池优化、前台服务、开机接收器、HyperOS 设置状态和最近心跳状态。
- `sms-code-validation-workflow`: 后续验证应增加重启、锁屏、后台、省电策略、force-stop、开机后第一条短信等设备场景。
## Impact
- 预期后续会修改:
- `app/src/main/AndroidManifest.xml`
- `app/src/main/java/com/smsreceive/app/MainActivity.java`
- 新增 `BootReceiver``SmsKeepAliveService``KeepAliveNotification``BackgroundSetupGuide``KeepAliveStateStore` 等 Java 类
- 可选新增 NDK/C++ heartbeat 实验文件,但默认不作为交付必需项
- 预期新增权限:
- `android.permission.RECEIVE_BOOT_COMPLETED`
- `android.permission.FOREGROUND_SERVICE`
- 视 targetSdk 和实现情况考虑 `android.permission.POST_NOTIFICATIONS`
- 视是否直接请求白名单考虑 `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
- 主要 API 和设置入口:
- `Intent.ACTION_BOOT_COMPLETED`
- `Intent.ACTION_LOCKED_BOOT_COMPLETED`
- `Intent.ACTION_MY_PACKAGE_REPLACED`
- `Context.startForegroundService`
- `Service.startForeground`
- `PowerManager.isIgnoringBatteryOptimizations`
- `Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS`
- `Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
- 小米自启动设置入口优先使用显式组件尝试,失败时回退应用详情页或系统设置页
- 该方案面向个人自用,不承诺 Play Store 合规;但仍不应隐藏通知、不应滥用后台执行、不应上传短信内容。
## Validation
- OpenSpec 文档结构完整,包含 `proposal.md``design.md``tasks.md` 和三个 capability spec。
- 方案必须能解释以下问题:
- 为什么 `onReceive` 可能收不到短信。
- 前台服务能解决什么,不能解决什么。
- 开机自启动在 Android 15 和 HyperOS 上的真实限制。
- C++/native 进程为什么只能作为诊断实验,不能作为可靠保活。
- 代码实现完成后不要求本轮编译,但后续至少需要完成单元测试和真机验证清单。
- 每次 commit 或 push 前必须检查 diff,避免引入非预期 EOF newline 变化。
@@ -0,0 +1,61 @@
## ADDED Requirements
### Requirement: Keep SMS monitoring visible through a foreground service
The app SHALL provide a user-controlled foreground service that keeps SMS monitoring diagnostics visible while the app is in the background.
#### Scenario: User enables keepalive
- **WHEN** the user enables background keepalive in the app
- **THEN** the app MUST start a foreground service with a persistent notification
- **AND** the app MUST persist that the user enabled keepalive
#### Scenario: Foreground service is running
- **WHEN** the keepalive service is active
- **THEN** it MUST periodically update a local heartbeat timestamp
- **AND** it MUST NOT perform long-running SMS database scans as its normal work
#### Scenario: User disables keepalive
- **WHEN** the user disables background keepalive in the app
- **THEN** the app MUST stop the foreground service
- **AND** it MUST remove or cancel the persistent keepalive notification
### Requirement: Restore keepalive after boot or package replacement
The app SHALL attempt to restore user-enabled keepalive after supported system lifecycle broadcasts.
#### Scenario: Device boot completes
- **WHEN** the app receives `BOOT_COMPLETED` or `LOCKED_BOOT_COMPLETED`
- **AND** the user previously enabled keepalive
- **THEN** the app MUST record the boot event
- **AND** it MUST attempt to start the keepalive foreground service
#### Scenario: App package is replaced
- **WHEN** the app receives `MY_PACKAGE_REPLACED`
- **AND** the user previously enabled keepalive
- **THEN** the app MUST attempt to restore the keepalive service
#### Scenario: Foreground service start is blocked
- **WHEN** Android or HyperOS rejects service startup from a boot receiver
- **THEN** the app MUST catch the failure
- **AND** it MUST store a diagnostic reason instead of crashing
### Requirement: Respect Android 15 foreground service limits
The app SHALL avoid using foreground service types that Android 15 restricts from `BOOT_COMPLETED` receivers for this keepalive feature.
#### Scenario: Service is declared in the manifest
- **WHEN** the keepalive service is declared
- **THEN** it MUST NOT be declared as `dataSync`, `camera`, `mediaPlayback`, `phoneCall`, `mediaProjection`, or `microphone` for the boot-started keepalive path
#### Scenario: Target SDK is upgraded
- **WHEN** the project target SDK is upgraded to Android 15 or higher
- **THEN** the boot-start keepalive behavior MUST be revalidated on the Xiaomi target device
### Requirement: Treat native keepalive as experimental only
The app SHALL NOT depend on a native daemon or C++ child process for reliable SMS delivery.
#### Scenario: Native heartbeat is added
- **WHEN** a native heartbeat experiment is enabled
- **THEN** it MUST be clearly labeled as diagnostic
- **AND** SMS reception MUST remain implemented through Android platform APIs
#### Scenario: Native process stops
- **WHEN** the native heartbeat stops because the app process or service is killed
- **THEN** the app MUST report the heartbeat loss as a keepalive limitation rather than attempting hidden restart loops
@@ -0,0 +1,65 @@
## ADDED Requirements
### Requirement: Diagnose missing SMS receiver delivery
The app SHALL explain why `SmsReceiver.onReceive` may not be called when a new SMS arrives.
#### Scenario: SMS permission is missing or unusable
- **WHEN** `RECEIVE_SMS` is not granted or is unusable because of restricted permission behavior
- **THEN** the app MUST report that the system SMS broadcast path is blocked by permission state
#### Scenario: App was force-stopped
- **WHEN** diagnostics indicate the app has not run since a user force-stop or package inactive state
- **THEN** the app MUST report that force-stopped apps are not expected to receive background broadcasts until manually opened
#### Scenario: HyperOS background policy is likely blocking delivery
- **WHEN** SMS appears in the inbox fallback path but no system SMS broadcast was recorded
- **THEN** the app MUST report that HyperOS autostart or battery policy may be blocking receiver delivery
- **AND** it MUST point the user to the background setup checklist
### Requirement: Correlate broadcast and inbox fallback results
The app SHALL record enough source metadata to distinguish SMS broadcast success from inbox fallback success.
#### Scenario: System broadcast receives SMS
- **WHEN** `SmsReceiver` handles `SMS_RECEIVED_ACTION`
- **THEN** the app MUST store the source as `system_sms_broadcast`
- **AND** update the latest broadcast receive timestamp
#### Scenario: Inbox observer sees SMS
- **WHEN** the ContentObserver or manual inbox reader finds a new SMS
- **THEN** the app MUST store an inbox source such as `sms_inbox_observer`, `sms_inbox_manual`, or `sms_inbox_polling`
- **AND** keep it distinct from system broadcast delivery
#### Scenario: Inbox succeeds after broadcast silence
- **WHEN** inbox fallback finds a verification SMS newer than the last recorded broadcast
- **THEN** the app MUST show a diagnostic message that the SMS was present in the inbox but was not delivered through the receiver path
### Requirement: Keep diagnostics local and privacy-minimized
The app SHALL diagnose receiver delivery without unnecessarily retaining full SMS bodies.
#### Scenario: Diagnostic record is saved
- **WHEN** the app stores SMS delivery diagnostics
- **THEN** it MUST store timestamp, source, sender summary, parse status, and failure reason
- **AND** it SHOULD avoid persistent storage of full SMS body unless a debug-only setting is enabled
#### Scenario: Logcat output is produced
- **WHEN** receiver or keepalive code logs diagnostic details
- **THEN** it MUST avoid logging full SMS body by default
- **AND** it MUST make source path and failure reason visible enough for debugging
### Requirement: Validate delivery across target device states
The app SHALL validate SMS receiver delivery under foreground, background, lockscreen, reboot, and force-stop states.
#### Scenario: Background keepalive is enabled
- **WHEN** the app is backgrounded and the keepalive notification is visible
- **THEN** receiving a test SMS MUST either update the latest result through `system_sms_broadcast`
- **OR** report a specific fallback/blocked state
#### Scenario: Device reboots
- **WHEN** the target Xiaomi phone reboots
- **AND** the user previously enabled keepalive and Xiaomi autostart
- **THEN** the app MUST record whether boot restore ran
- **AND** whether the first post-boot SMS reached `SmsReceiver`
#### Scenario: User force-stops the app
- **WHEN** the user force-stops the app from system settings
- **THEN** the app MUST NOT claim reliable SMS delivery until the user manually opens the app again
@@ -0,0 +1,56 @@
## ADDED Requirements
### Requirement: Guide the user through Xiaomi background setup
The app SHALL provide explicit guidance and shortcuts for the Xiaomi/HyperOS settings required for reliable background behavior.
#### Scenario: User opens background setup
- **WHEN** the user opens the app's background setup section
- **THEN** the app MUST show checklist items for SMS permission, autostart, battery unrestricted mode, notification visibility, and keepalive service state
#### Scenario: Xiaomi autostart settings shortcut is available
- **WHEN** the app can resolve a Xiaomi/HyperOS autostart settings activity
- **THEN** it MUST open that settings page from the setup UI
#### Scenario: Xiaomi autostart shortcut is unavailable
- **WHEN** the explicit Xiaomi settings activity cannot be resolved or launched
- **THEN** the app MUST fall back to application details or general settings
- **AND** it MUST keep the manual setup checklist visible
### Requirement: Represent manual-only settings honestly
The app SHALL distinguish settings that can be detected through Android APIs from settings that require manual user confirmation.
#### Scenario: Battery optimization state is queried
- **WHEN** the app checks Android battery optimization status
- **THEN** it MUST use `PowerManager.isIgnoringBatteryOptimizations` where available
- **AND** display whether Android reports the app as ignoring battery optimizations
#### Scenario: Xiaomi autostart state cannot be read
- **WHEN** no stable public API exists to read the Xiaomi autostart switch
- **THEN** the app MUST ask the user to manually confirm completion instead of pretending to detect it
#### Scenario: Battery unrestricted state cannot be read
- **WHEN** no stable public API exists to read the HyperOS per-app battery unrestricted option
- **THEN** the app MUST ask the user to manually confirm completion
### Requirement: Provide battery optimization actions
The app SHALL provide Android-standard actions for battery optimization setup.
#### Scenario: User opens battery optimization settings
- **WHEN** the user taps the battery optimization settings action
- **THEN** the app MUST launch `Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS` when available
#### Scenario: User requests direct exemption
- **WHEN** the app offers a direct ignore-battery-optimization request
- **THEN** it MUST use `Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
- **AND** explain that exemption still does not guarantee unlimited background execution
### Requirement: Preserve user control
The app SHALL keep background keepalive opt-in and reversible.
#### Scenario: First app launch after install
- **WHEN** the app is launched for the first time
- **THEN** background keepalive MUST be disabled until the user enables it
#### Scenario: User turns off keepalive
- **WHEN** the user turns off keepalive
- **THEN** boot restore MUST no longer restart the keepalive service
@@ -0,0 +1,84 @@
## 1. Spec And API Validation
- [x] 1.1 阅读现有 `SmsReceive` 实现,确认短信广播、收件箱兜底和诊断 UI 的当前状态
- [x] 1.2 查询 Android 15 前台服务、Doze/App Standby、`RECEIVE_SMS` hard restricted、小米后台自启动相关资料
- [x] 1.3 生成后台保活 OpenSpec proposal、design、tasks 和 capability specs
- [x] 1.4 用 `npx openspec validate add-xiaomi-background-keepalive` 校验规格结构
- [ ] 1.5 后续提交或 push 前检查 diff,避免引入非预期 EOF newline 变化
## 2. Manifest And Permission Plan
- [x] 2.1 增加 `RECEIVE_BOOT_COMPLETED` 权限
- [x] 2.2 增加前台服务所需权限,按当前 targetSdk 兼容处理
- [ ] 2.3 如 targetSdk 升到 33+,补充 `POST_NOTIFICATIONS` 申请和诊断
- [x] 2.4 如使用直接请求电池优化白名单,增加 `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`
- [x] 2.5 声明 `BootReceiver`,覆盖 `BOOT_COMPLETED``LOCKED_BOOT_COMPLETED``MY_PACKAGE_REPLACED`
- [x] 2.6 声明 `SmsKeepAliveService`,避免声明 Android 15 从 boot 禁止启动的前台服务类型
## 3. Keepalive Service
- [x] 3.1 新增 `SmsKeepAliveService`
- [x] 3.2 服务启动后立即创建通知渠道和低打扰常驻通知
- [x] 3.3 服务在规定时间内调用 `startForeground`
- [x] 3.4 服务定期写入心跳状态,不执行耗时短信扫描
- [x] 3.5 服务停止或异常时写入明确诊断原因
- [x] 3.6 增加 UI 按钮开启/关闭常驻保活,并持久化用户选择
## 4. Boot And Package Restore
- [x] 4.1 新增 `BootReceiver`
- [x] 4.2 收到开机或包替换广播时记录事件和时间
- [x] 4.3 如果用户已开启保活,尝试启动 `SmsKeepAliveService`
- [x] 4.4 捕获前台服务启动限制异常并写入 `KeepAliveState`
- [x] 4.5 在 UI 展示最近开机事件和服务恢复结果
## 5. Xiaomi / HyperOS Setup Guide
- [x] 5.1 增加标准应用详情页入口
- [x] 5.2 增加 Android 电池优化设置入口
- [x] 5.3 增加可选的直接请求忽略电池优化入口
- [x] 5.4 增加小米后台自启动设置入口,显式组件失败时 fallback
- [x] 5.5 增加“我已开启自启动”人工确认状态
- [x] 5.6 增加“我已设置省电无限制”人工确认状态
- [x] 5.7 UI 明确说明人工确认项无法通过稳定公开 API 完全读取
## 6. SMS Receiver Delivery Diagnostics
- [x] 6.1 记录最近一次 `system_sms_broadcast` 到达时间
- [x] 6.2 记录最近一次 `sms_inbox_observer``sms_inbox_manual``sms_inbox_polling` 命中时间
- [x] 6.3 当收件箱兜底发现新验证码但广播未到达时,展示“疑似短信广播未投递”
- [x] 6.4 在 UI 中列出 `onReceive` 不触发的排查清单
- [x] 6.5 增加 logcat 输出,区分权限缺失、body 为空、parser 失败、广播未到达
## 7. Optional Native Experiment
- [x] 7.1 第一轮 Java 保活方案真机验证前,不实现 native fork/daemon
- [ ] 7.2 如用户仍要求尝试,新增 NDK heartbeat 实验开关
- [ ] 7.3 native heartbeat 只写本地诊断状态,不承担短信监听职责
- [ ] 7.4 文档记录 native 子进程被系统同组清理的限制
## 8. Tests
- [ ] 8.1 为 `KeepAliveStateStore` 增加状态读写测试
- [ ] 8.2 为 Boot action 处理逻辑增加单元测试
- [ ] 8.3 为设置 intent fallback 增加测试或可验证日志
- [x] 8.4 保持现有验证码解析测试通过
- [x] 8.5 不要求本轮编译;代码完成后再按用户要求通知
## 9. Xiaomi 12S / HyperOS 3 Device Validation
- [ ] 9.1 手动开启小米自启动
- [ ] 9.2 手动设置省电策略为无限制
- [ ] 9.3 开启常驻通知,后台 30 分钟后发送验证码短信
- [ ] 9.4 锁屏 30 分钟后发送验证码短信
- [ ] 9.5 重启手机,确认保活服务是否自动恢复
- [ ] 9.6 重启后未打开 app 直接发送第一条短信,记录广播是否到达
- [ ] 9.7 手动 force-stop 后发送短信,确认不承诺接收,并记录诊断表现
- [ ] 9.8 如 `onReceive` 不到但收件箱有短信,记录 HyperOS 设置、权限状态和 logcat
## 10. Delivery Criteria
- [x] 10.1 OpenSpec validate 通过
- [x] 10.2 相关上下文逻辑分析通过
- [x] 10.3 相关测试通过
- [x] 10.4 代码实现完成后通知用户,不强制编译