Skip to content

[android][enhancement] 设计可恢复的后台录音与系统托管服务生命周期 #1084

Description

@HKLHaoBin

背景

希望 OpenLess 在 Android 上实现类似李跳跳、闹钟类应用的后台工作能力:用户在 OpenLess 可见时启动听写后,即使关闭 Activity、按 Home,甚至在部分设备上划掉最近任务,录音/悬浮入口仍能尽量继续工作;如果进程确实被系统回收,也应能恢复或安全收尾。

这里的目标不是绕过 Android 的 force-stop、Android 13+ Task Manager Stop 或厂商的强制冻结,而是采用 Android 官方支持的系统托管机制,并让进程死亡后可恢复。

调研结论

1. 不建议复制 Shizuku 的保活方式

Shizuku 的 shizuku_server 是由 ADB UID 2000 或 root UID 0 通过 app_process 启动的独立进程。管理器 Activity 被划掉,并不等于该独立进程被杀掉。

普通 OpenLess APK 无法复制这个身份。连接 Shizuku 可以提供特权 Binder/ UserService 能力,但不会改变 OpenLess 主进程的 UID、前台服务生命周期或麦克风权限,也不适合承载完整 Tauri/WebView/Core。

参考:

2. 李跳跳的核心参考价值是 AccessibilityService

李跳跳的公开资料和仿制项目能够确认的核心机制是 AccessibilityService:用户在系统设置中启用服务后,由 Android 系统绑定并在后台分发界面事件。它不是 Root/ADB,也不是普通 Activity 常驻。

这类服务仍然受用户关闭无障碍、force-stop、低内存和厂商后台策略影响;“前台服务、锁定任务卡片、关闭电池优化、自启动白名单”等属于增强兼容性的组合措施,不是永久保活保证。

参考:

3. 闹钟类应用不是一直保持进程

闹钟应用通常采用:

AlarmManager.setAlarmClock()
  -> 系统到点唤醒 BroadcastReceiver
  -> 启动前台服务
  -> 播放声音/振动/显示通知或锁屏界面

应用在两个闹钟之间可以完全不运行。setAlarmClock() 能够在 Doze 中唤醒,但不是常驻心跳;重启后还需要重新注册,force-stop 后也不能保证继续触发。

开源参考:

OpenLess 当前状态与缺口

  • android/kotlin/OpenLessAccessibilityService.kt 已经是系统绑定的无障碍入口,并且 Manifest 使用了独立的 :accessibility 进程。
  • OpenLessApplication.kt 在 Activity 进入后台时显示 Overlay,但当前 ACTION_SHOW 使用普通 startService;Overlay Service 只有录音时才提升为 microphone Foreground Service。
  • OpenLessOverlayService.kt 返回 START_STICKY,但没有 onTaskRemoved();系统重建时的 null Intent 也没有恢复会话逻辑。
  • Overlay Service 默认与 Tauri 主进程同进程。src-tauri/src/mobile_runtime.rs 只在 Tauri setup 中初始化 Core,src-tauri/src/android/native_bridge.rsCORE_BACKEND 也是进程内 OnceLock,因此冷启动 Overlay/独立进程时不能直接假设 Rust Core 已存在。
  • crates/openless-core/src/external_audio.rs 已有外部 PCM 输入和录音归档抽象,可以作为 Android 原生录音服务与 Core 之间的适配基础,但目前 session 主要仍在内存中。
  • 当前没有用于后台恢复的持久 session journal、BOOT_COMPLETED reschedule 或 headless Android 录音入口。

最可行的方案

阶段 1:用户可见启动的 microphone Foreground Service

  1. 用户在 Activity 可见时点击开始听写。
  2. 立即启动/提升 Android microphone Foreground Service,并显示持续通知。
  3. 前台服务负责录音生命周期和悬浮控制;Tauri/WebView 只负责 UI 与配置。
  4. onStartCommand(null)、Service 重建、录音结束、取消、ASR 失败都必须有明确状态转换和清理逻辑。
  5. 不从 BOOT_COMPLETED 自动启动麦克风;Android 14+ 对这类启动有明确限制。

Android 官方限制:

  • Android 12+ 限制后台启动 Foreground Service;
  • Android 14+ microphone FGS 需要 RECORD_AUDIOFOREGROUND_SERVICE_MICROPHONE,且通常必须在 Activity 可见时启动;
  • Foreground Service 也不能绕过 Task Manager Stop、force-stop 或厂商杀进程。

参考:https://developer.android.com/develop/background-work/services/fgs/restrictions-bg-start

阶段 2:持久化 session,做到“被杀后可恢复”

  • RECORDING / FINALIZING / ORPHANED / COMPLETED 等状态写入 App 私有存储。
  • PCM/WAV 采用分段或追加 journal,避免进程被杀时整段录音丢失。
  • Service 重建或下一次用户启动时扫描未完成 session,完成 WAV 收尾、恢复 ASR 或给出明确失败状态。
  • 无障碍进程不依赖 WebView;主进程不可用时先缓存事件,Core 恢复后再同步。

阶段 3:仅在真机证明必要时拆分录音进程

第一阶段先复用现有 Core,降低实现风险。如果测试证明 Recents 划掉会连同主进程杀死,再将“录音采集 + 持久化 journal”拆到 :recorder 进程。

不能只给现有 Overlay Service 增加 android:process:需要为该进程设计 headless 初始化、AIDL/文件队列或其他 IPC,并处理 ndk-context、Core 初始化、凭证和存储并发问题。录音服务应能在没有 WebView 的情况下工作,Tauri Core 可在恢复后消费已归档音频。

明确不纳入目标

  • 不把 Shizuku UserService 当作 OpenLess 的常驻守护进程。
  • 不使用 AlarmManager 周期性心跳模拟保活。
  • 不尝试绕过 force-stop、Task Manager Stop 或系统的 stopped package 状态。
  • 不用 AccessibilityService 伪装通用录音守护进程;它只承担跨应用 UI 事件与输入辅助。
  • 不为 OpenLess 申请仅用于保活的精确闹钟或 Full-screen Intent 权限。

验收建议

  • Activity 关闭、按 Home 后,已由用户启动的录音仍能持续,且有前台通知。
  • 在支持的设备上划掉最近任务后,Overlay/录音行为有明确、可重复的结果。
  • 模拟普通进程回收后,session 能恢复或安全收尾,WAV 不损坏。
  • AccessibilityService 在主 Activity 不可见时仍能接收事件,主进程未就绪时不崩溃。
  • Android 13/14/15+ 至少覆盖 AOSP/Pixel、HyperOS/MIUI、ColorOS 等环境。
  • Task Manager Stop 和 force-stop 的行为明确记录为“系统终止,不承诺绕过”。

相关 Issue

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions