Zorv AI 终端技术架构与开发指南(新版)

1 阅读15分钟

文档版本:v1.0.72(对应 app/build.gradle.ktsversionName / versionCode
修订日期:2026-09-01
适用范围:Android 应用 com.ai.assistance.quro(品牌 ZorvAI),基于 proot + Ubuntu 24.04 ARM64 的用户态 Linux 终端
替换关系:本文档取代旧版《Zorv AI 终端技术架构与开发指南.md》(v1.0.67 / 2026-08-29),重点修正了旧版中已过时的内容(见文末「与旧版的主要差异」)。
开源地址github.com/Quor-a/Zorv…


目录


1. 概述

Zorv AI 终端是一套无 root、无 Shizuku 的 Android 内置命令行环境,核心能力是:

  • 在 Android 上跑起一个完整的 Ubuntu 24.04 (ARM64) 用户空间(proot 沙箱),从而获得 python3aptnodepipnpm、任意写文件等 Linux 能力;
  • 当 proot/Ubuntu 不可用时,优雅降级到设备自带 /system/bin/sh(Toybox),保证终端永远可用;
  • 提供统一的「默认共享 shell 会话」,被 AI 工具层 / 终端界面 / CMS 开发环境 三方共用;
  • 通过 5 路接入(AIDL Binder / ACI 服务 / ContentProvider / DeepLink / Broadcast)把终端能力开放给外部(含主程序自身与 ACI 控制端);
  • 通过前台保活服务保证息屏 / 切后台时会话不被系统回收。

1.1 关键设计变更(相对旧版)

项目旧版(v1.0.67)新版(v1.0.72)
会话引擎依赖 Termux terminal-emulator + 原生 PTY自包含 QuroShellSession:常驻 shell 进程 + 管道 stdin + 哨兵协议,移除 Termux/PTY 依赖(terminal/vt 提供可选真·终端渲染)
会话管理AI/UI/CMS 各自起 proot 进程,无共享统一 QuroTerminalSessionManager 持有「默认共享会话」,list/create/switch/destroy
宿主 home 目录getExternalFilesDir(null)/sandbox-home(外部 FUSE,proot 子进程写/符号链接受限)context.filesDir/linux-sandbox/sandbox-home(app 内部真实文件系统,支持 symlink 与 Unix 权限,思路同 Termux $PREFIX
DNS/etc/hosts 静态清单三层方案:hosts 静态预解析 + 应用层 DoH(443) 预解析 + dnscrypt-proxy 通用 DoH 代理(按实测延迟自动选最优上游)
类名QuroTerminalController / QuroShellSession / QuroLinuxEnv同名延续;新增 QuroTerminalSessionManagerQuroTerminalSentinelQuroHostBridge

2. 整体架构

┌──────────────────────────────────────────────────────────────────────┐
│                           Zorv AI(主进程 / UI 进程)                      │
│                                                                        │
│  UI 层                                                                 │
│   ├─ QuroNovaTermScreen.kt  ── 终端 UI(Compose,挂 QuroShellSession + VT)│
│   ├─ QuroDevEnvScreen.kt     ── 开发环境(CMS/语言运行时)UI              │
│   └─ ChatScreen.kt           ── 对话内 terminal_exec / aci 调用           │
│                                                                        │
│  控制层                                                                 │
│   ├─ QuroTerminalController.kt   ── 全局控制器(session 委托给管理器)     │
│   ├─ QuroTerminalSessionManager  ── 默认共享会话 + 列表/切换/销毁          │
│   ├─ QuroShellSession.kt         ── 自包含交互式 shell(核心)            │
│   ├─ QuroTerminalSentinel.kt     ── 哨兵协议(退出码/工作目录检测)        │
│   └─ QuroHostBridge.kt           ── 原生 host 后端控制桥(qurohost)      │
│                                                                        │
│  沙箱层                                                                 │
│   └─ QuroLinuxEnv.kt  ── proot 启动 / rootfs 管理 / DNS 三层 / 源镜像     │
│                                                                        │
│  接入层(5 路)                                                          │
│   ├─ QuroTerminalAciService.kt  ── ACI 受控端(12 能力,前台保活)       │
│   ├─ TerminalProvider.kt          ── ContentProvider(authority=...terminal)│
│   ├─ TerminalDeepLinkHandler.kt   ── quro://terminal/... DeepLink         │
│   ├─ TerminalIntentHandler.kt     ── 显式/隐式 Intent                     │
│   └─ TerminalBroadcastReceiver.kt  ── Broadcast(有序/带权限)            │
└───────────────┬──────────────────────────────────────┬─────────────────┘
                │ startForegroundService                 │ bindService
                ▼                                        ▼
┌───────────────────────────────┐        ┌──────────────────────────────────┐
│ QuroTerminalKeepAliveService  │        │  QuroTerminalAciService (同进程)   │
│ (specialUse 前台,15s 巡检)   │        │  └─ 继承 BaseAidlAciService         │
│  → fork shell 子进程(保活)    │        └──────────────────────────────────┘
└───────────────┬───────────────┘
                │ 常驻进程树
                ▼
┌───────────────────────────────┐        ┌──────────────────────────────────┐
│ proot -R rootfs /bin/sh        │◀───┐   │  Ubuntu 24.04 ARM64 rootfs         │
│ (LINUX 模式,完整用户空间)     │    │   │  (linux-sandbox/rootfs)           │
└───────────────────────────────┘    │   └──────────────────────────────────┘
       若 proot 不可用 → /system/bin/sh(DEVICE 模式,Toybox)

3. 核心组件

3.1 QuroTerminalController(全局控制器)

object QuroTerminalController —— 不再自行持有会话,而是把「默认共享会话」委托给 QuroTerminalSessionManager

  • session:getter,返回 QuroTerminalSessionManager.defaultSession
  • createSession(ctx) / ensureSession(ctx)必须在后台线程调用runBlocking(Dispatchers.IO)),会话创建含 rootfs 安装与 proot 启动(秒级~分钟级),主线程调用会 ANR;
  • sendToShell(cmd):用户回车,等价于 session.sendCommand
  • sendRaw(text) / sendKey(seq):喂交互式程序(REPL)或特殊按键(Tab/ESC/^D 不补换行);
  • runCommand(cmd, timeoutMs, ctx):非交互式一次性执行(AI terminal_exec 设备回退用),与活动会话解耦;Linux 环境就绪时走 proot(runCommandInLinux),否则回退 /system/bin/shrunCommandInDevice);
  • interrupt(ctx):两阶段中断(软 ETX → 硬杀重建并 cd 回原目录)。

返回结构 ShellResult(output, exitCode, timedOut, error),AI 工具层可如实上报 exit_code,不再靠正则猜提示语。

3.2 QuroShellSession(自包含交互式 shell,v127 重写核心)

class QuroShellSession —— 彻底移除 Termux/PTY 依赖。设计要点:

  • 常驻一个 shell 进程(LINUX 模式为 proot -R rootfs -b /system ... /bin/sh;DEVICE 模式为 /system/bin/sh),把命令写进其 stdin,把 stdout/stderr 按行读入 Compose SnapshotStateList 滚动缓冲区;

  • 为何不用 PTY:Termux terminal-emulator 在 Compose 布局期会因 mRenderer.mFontWidth 空指针崩溃;且 stdin 为管道时内核不会把 ^C 翻译为 SIGINT;

  • 哨兵协议QuroTerminalSentinel):每条命令后追加一行

    printf '\n\036<随机token>:%d:%s\036\n' "$?" "$PWD"
    

    读取端识别哨兵行即可拿到上条命令的退出码当前工作目录,并复位 busy、打印新提示符。哨兵写入 stderr 再经 redirectErrorStream(true) 合并,避免 stdout 块缓冲卡住完成信号;

  • E-8:哨兵 token 每会话随机(QuroTerminalSentinel.newToken()),避免 echo QURO_DONE 被误判为命令结束;

  • E-9interrupt() 两阶段——先写 ETX(\u0003)(对读原始 stdin 的程序有效),等待 INTERRUPT_GRACE_MS=1200ms;软中断失败返回 false,由 Controller 杀进程并重建 + cd 回原目录;

  • VM 真 TTY 模式passthroughEcho=true 时回显/提示符/信号全部由 guest shell 完成,本层只透传输入、不注入哨兵;

  • 可选 VT 渲染var vt: TerminalScreen?,非 null 时 drain 把**原始(含 ANSI 转义)**字节喂给 terminal/vt 引擎,渲染真·终端(颜色/光标/清屏);lines 仍同步维护纯文本用于导出兜底;

  • 终端内 ACI 命令:拦截 aci / aci ...,在本层(Kotlin 侧)经 AciNativeBridge.callJson 调 ACI 全部能力(进程内原生代码则直接用 libacihost.so);

  • exportLog():导出滚动缓冲到 Documents/QuroDocs/terminal_<ts>.log(E-10)。

ShellMode 枚举:DEVICE / LINUX / VM

3.3 QuroTerminalSessionManager(统一会话管理器)

object QuroTerminalSessionManager —— 解决「AI/UI/CMS 三方各自起 proot 进程、无共享会话」的碎片化:

  • 持有 defaultEntry(默认共享会话,被 AI/CMS/使用者共用)、extras(额外会话)、uiEntry(UI 界面登记的 Termux PTY 会话,仅展示);
  • ensureDefault(ctx, installIfMissing)installIfMissing=true 时若后端未就绪则跟随安装(打开终端/用户操作);false 时不开机误下载(自启动/保活用);内部 withContext(Dispatchers.IO) 切线程,避免 UI 调用方在 Main 调度器上 ANR;
  • createSession(ctx, name) / switchDefault(id) / destroySession(id) / killDefault()
  • listSessions() / getSession(id) / getShellSession(id)
  • load(ctx):启动时载入持久化会话元数据(quro_terminal_sessions.json,进程重启后进程已消亡,alive=false);
  • registerUiSession/ unregisterUiSession:UI 终端 PTY 会话登记(不影响默认 shell)。

create() 永不抛、永不为 null:任何异常都被捕获降级到 createDevice,避免终端 UI 拿到异常崩溃、永久停在「正在启动终端…」。

3.4 VT 渲染引擎(terminal/vt)

terminal/vt/ 包提供可选真·终端渲染,替换「纯文本 LazyColumn」:

  • VtParser.kt:VT100/xterm 转义序列解析;
  • TerminalScreen.kt:行缓冲 + 光标 + 颜色模型,snapshot() 产出 TerminalSnapshot 给 Compose;
  • QuroTerminalPane.kt:Compose 画布渲染终端屏幕;
  • QuroTerminalInputView.kt / TerminalKeys.kt / TerminalMouse.kt:输入、软键盘功能键、鼠标;
  • TerminalSnapshot.kt:不可变快照(跨线程安全,IO 线程 publishVt → 主线程重组)。

QuroTerminalJNI.javalibtermux-terminalcreateSubprocess/setPtyWindowSize/...)作为旧 Termux PTY 路径保留,新默认引擎已不依赖它;uiEntry 登记的 UI Termux PTY 会话仍可能用到。

3.5 终端 UI

  • ui/QuroNovaTermScreen.kt:Nova 终端 UI(Compose),挂 QuroShellSession + VT 引擎;
  • ui/QuroDevEnvScreen.kt:开发环境 UI(CMS / 语言运行时 Python·Node·Go 等的安装与状态面板),复用默认共享会话与 QuroLinuxEnv 后端;
  • ui/QuroTermuxTerminalScreen.kt:Termux 兼容前端(旧路径,UI 终端 PTY 会话登记用)。

4. Linux 沙箱(proot + Ubuntu 24.04)

核心文件 core/linux/QuroLinuxEnv.kt

4.1 目录布局(关键修正)

<app filesDir>/linux-sandbox/
├── rootfs/            # Ubuntu 24.04 ARM64 rootfs(网络下载 gz,解压)
│   ├── etc/hosts       # 注入 # quro-dns-bootstrap 静态映射
│   ├── etc/resolv.conf # nameserver 127.0.0.1(DoH 代理优先)
│   ├── etc/nsswitch.conf # hosts: files dns
│   └── usr/sbin/dnscrypt-proxy  # 通用 DoH 代理(apt 安装)
├── sandbox-home/      # 宿主侧 /root 绑定目录(★ app 内部存储,真实文件系统)
├── tmp/
└── usr/bin/           # bash / busybox 等补充二进制
  • sandboxDir(ctx) = ctx.filesDir/linux-sandbox
  • rootfsPath(ctx) = sandboxDir/rootfs
  • homePath(ctx) = sandboxDir/sandbox-home(proot 内即 /root)。旧版绑外部存储 getExternalFilesDir/sandbox-home,但外部 FUSE/sdcardfs 对 proot 子进程有写与符号链接限制(SELinux 域 + FUSE 不支持 symlink),导致 python3 -m venv 静默失败;新版改绑 app 内部存储,同 UID 同 SELinux 域,可自由读写与建 symlink(思路同 Termux $PREFIX);
  • sharedStorageHostDir(ctx):仅当已获「所有文件访问」权限(MANAGE_EXTERNAL_STORAGE / 旧版 READ|WRITE_EXTERNAL_STORAGE)才返回 /sdcard 宿主路径并真正 bind,保证「挂载即真实可用」;
  • prootPath(ctx) = nativeLibraryDir/libproot.so(缺失时从 assets 解压兜底)。

4.2 shell 启动

QuroLinuxEnv.shellLaunch(ctx): Pair<String, List<String>>?

  • probeLenient(ctx) 宽松探测(避免旧严格 probe 把可启动的 proot 误降级成 /bin/sh);

  • 标准 proot 路径(非 chroot):

    libproot.so --rootfs=<rootfs> --link2symlink
      --bind=/dev --bind=/dev/urandom:/dev/random
      --bind=/proc --bind=/sys
      --bind=<homePath>:/root --bind=<tmp>:/tmp
      [--bind=<sdcard>:/sdcard]   # 仅当权限满足
      -0 -w /root /bin/sh
    
  • isChrootAvailable(已 root):改用 su -c 'chroot ...' 脚本(bind /dev /proc /sys /root /sdcard);

  • shellEnv(ctx):注入 TERM=xterm-256color HOME=/root LANG=C.UTF-8 PATH LD_LIBRARY_PATH PROOT_LOADER=<loaderPath> PROOT_TMP_DIR

  • prepareRuntimeExtras(ctx, rootfs):每次命令前刷新运行时资产(resolv.conf 用设备 DNS、getprop 垫片写 /etc/quro_props.prop),避免安装时一次性快照过期。

QuroShellSession.create → createLegacy:优先 shellLaunch(proot/LINUX),失败降级 createDevice(DEVICE /system/bin/sh)。

4.3 DNS 三层方案(绕开被阻断的 53 端口)

Android 上容器内 53 端口常被运营商/系统 stub 阻断,导致 apt/pip/npm 全部超时。三层逐级兜底,全链路非致命 + 可自愈

层 1 — 宿主侧 /etc/hosts 静态预解析(bootstrapHosts

curatedDevHosts(ctx) 列出开发镜像/CDN 主机(apt 源、pip、npm/yarn/pnpm、github、go、cargo/rust、maven/gradle、dl.google.com、node/deno、conda/docker,以及用户所选 apt 镜像 + UBUNTU_APT_MIRRORS)。宿主 InetAddress.getAllByName 解析为 IPv4,写入 rootfs/etc/hosts,用标记行 # quro-dns-bootstrap 隔离自写条目,不破坏系统/用户其它 hosts。宿主全失败则保留上次结果。

层 2 — 应用层 DoH(443) 预解析(bootstrapHostsByDoH

resolveHostIpsByDoHcuratedDevHosts 逐主机走 DOH_JSON_ENDPOINTS(Google 风格 application/dns-json),按国内可达性排序、自动选首个成功者

https://dns.alidns.com/resolve?name=%s&type=A      # 阿里(国内首选)
https://doh.360.cn/resolve?name=%s&type=A          # 360
https://119.29.29.29/resolve?name=%s&type=A        # 腾讯(IP 直连)
https://dns.google/resolve?name=%s&type=A          # Google(兜底)
https://cloudflare-dns.com/dns-query?name=%s&type=A# Cloudflare(兜底)

完全不经过 Android 系统 DNS stub 与容器内 53 端口,即便 Private DNS 异常也能按 IP 直连。结果同样落到 # quro-dns-bootstrap 段,与层 1 幂等合并。

层 3 — dnscrypt-proxy 通用 DoH 代理(ensureDnsProxy

  • 监听 127.0.0.1:53,向上走 DoH(443) 解析任意域名,彻底绕开 53 端口;

  • upstream 候选 DOH_UPSTREAMS(国内可达优先,Google/Cloudflare 兜底):

    alidns / dot360 / baidu / tencent(119.29.29.29) / google(8.8.8.8) / cloudflare(1.1.1.1)
    
  • buildDohStamp(providerName, path, bootstrapIp):构造 sdns:// stamp(proto=0x02props uint16 LE=0Addr 留空,BootstrapIP 内嵌解析器 IP),使 proxy 直连解析器 IP 并以 providerName 作 SNI,不依赖被阻断的系统 DNS 引导

  • measureDohLatency(bootstrapIp):测 :443 TCP 建连延迟(1.5s 超时),把 server_names 按实测延迟排序 → 自动选最优上游;全部不可达退化为默认顺序;

  • buildResolvConf(ctx)nameserver 127.0.0.1 + 设备真实 DNS(前 2)兜底 + options timeout:1 attempts:2

  • ensureNsswitch:保证 hosts: files dns

  • 启动后 curl -sI --max-time 5 https://dns.alidns.com 端到端探测,失败则保留 127.0.0.1 首选项 + 层 1/2 /etc/hosts 兜底;

  • 代理每次命令前 prepareRuntimeExtras 重跑,被回收会在下条命令前自动重启。


5. 前台保活(QuroTerminalKeepAliveService)

QuroTerminalKeepAliveService : ServicespecialUse 前台服务,CHANNEL_ID=quro_terminal_channelNOTIF_ID=9529):

  • onCreatestartForeground(NOTIF_ID, ..., SPECIAL_USE),SPECIAL_USE 失败降级 DATA_SYNC
  • startLoop:立即 ensureSessionSafe(installIfMissing=false)(自启动不下载)+ 拉起 ACI 服务;每 PERIOD_MS=15_000ms 巡检:会话死亡重建、ACI 服务在跑;
  • 在本服务进程内 fork shell 子进程(heldSession),服务存活 = shell 子进程存活,息屏/切 App/后台不被杀;
  • onStartCommandACTION_STOPstopSelf();系统意外重建时确保巡检循环仍运行(START_STICKY);
  • onDestroyloopJob.cancel() + heldSession?.destroy()

6. ACI 受控端(QuroTerminalAciService)

QuroTerminalAciService : BaseAidlAciService(前台服务 specialUseNOTIF_ID=9530)—— 继承 ACI 基类,注册 12 个能力

#能力说明
1exec执行命令,返回 exit_code/output/error/timed_outFLAG_BACKGROUND,默认超时 14s,交互 30s)
2create_session创建新会话,返回 session_id
3destroy_session销毁指定会话
4send_input向会话发原始输入(交互式)
5get_session_status会话实时状态(alive/busy/cwd/last_exit
6list_sessions列出所有会话
7set_session_env设置环境变量(当前仅创建时生效,运行时受限)
8get_session_env会话环境概要
9list_capabilities列出所有能力
10get_service_status服务运行状态(pid/sessions_count/uptime/version
11get_audit_log调用审计日志(最近 1000 条,CopyOnWriteArrayList
12help帮助信息
  • 权限:onCheckPermission 仅放行自身包名与 com.ai.assistance.quro(ZorvAI 控制端);
  • Token:onVerifyToken 自身调用跳过,否则走 AciTokenVerifier.verify
  • 分发:onCall(req)capability 路由到 handleXxx,每次调用 recordAudit(seq/时间戳/调用方/能力/耗时)写入审计日志;
  • 接入方式声明(见 help):AIDL Binder(ACTION_BIND)/ ContentProvider / DeepLink / Broadcast / Intent Activity。

7. 命令路由与四路 IPC(外部接入)

除 ACI 服务(AIDL)外,终端还暴露四路标准 Android 接入,均落到同一套 QuroTerminalController / QuroTerminalSessionManager

路径实现标识 / Action权限
ContentProviderTerminalProvidercontent://com.ai.assistance.quro.terminal/{sessions|sessions/{id}|sessions/{id}/output|exec|status|capabilities}READ_TERMINAL / WRITE_TERMINAL(按 Binder.getCallingUid() 校验)
DeepLinkTerminalDeepLinkHandlerquro://terminal/{exec|sessions|status|create}?cmd=...&timeout=...同应用内
IntentTerminalIntentHandlercom.ai.assistance.quro.action.TERMINAL_*(EXEC/STATUS/SESSIONS/CREATE_SESSION/DESTROY_SESSION/SEND_INPUT/GET_OUTPUT/PICK_SESSION) + ACTION_SEND/ACTION_VIEW显式 Intent 绑组件;隐式仅 Activity/Broadcast
BroadcastTerminalBroadcastReceiver同上 Action(有序广播 sendOrderedBroadcast 支持结果传播)SEND_TERMINAL_BROADCAST / RECEIVE_TERMINAL_BROADCAST

四路统一语义:execQuroTerminalController.runCommand;会话管理经 QuroTerminalSessionManager。ContentProvider 的 query/insert/update/delete 标准 CRUD 映射到会话列表/输出/状态/能力/执行。


8. 终端内 ACI 命令

在终端 UI 直接敲 aci 即可使用 ACI 全部能力(Kotlin 侧拦截,不经 proot):

  • aci list / aci targets:列出所有受控端及能力;
  • aci call <包名> <能力> [参数JSON]:调用指定受控端能力(终端内敲命令即视为已确认,confirmed=true);
  • aci help:帮助。

示例:

aci call com.ai.assistance.quro intent {"mode":"activity","action":"android.intent.action.VIEW"}
aci call com.ai.assistance.quro provider {"uri":"content://sms/inbox","op":"query","limit":"5"}

9. 开发指南与 FAQ

Q1:终端一直停在「正在启动终端…」?

A:QuroShellSession.create 永不抛、永不为 null,若仍卡住通常是 ensureDefault 在 Main 调度器上被调用未切 IO 线程(已用 withContext(Dispatchers.IO) 修复)。检查是否从 rememberCoroutineScope(Main)直接调且未走 suspend 版本。

Q2:Linux 命令(apt/python3)不可用?

A:当前是 DEVICE 模式(proot 未启用)。点顶栏「安装 Linux 环境」或对话发 linux:installrunCommand/ensureDefault(installIfMissing=true) 会自动装 rootfs。

Q3:APT 更新超时 / pip 装包失败?

A:走 DNS 三层(层 1/2 静态 hosts + 层 3 dnscrypt-proxy)。若代理探测失败,先确认设备能联网且已装 dnscrypt-proxy(setup 阶段 apt 安装);curl -sI https://dns.alidns.com 国内应可达。

Q4:python venv / CMS 引擎目录建不了?

A:旧版绑外部存储导致 /root 不可写;新版 homePath 已改绑 app 内部 linux-sandbox/sandbox-home,确保同 UID 可读写 + 支持 symlink。

Q5:如何新增一个 ACI 能力?

A:在 QuroTerminalAciService.onCreateCapabilitiesCapability.create(...) 并在 onCallwhen 加分支;审计日志自动记录。

Q6:前台服务类型?

A:specialUse(Android 14+ 需要 FOREGROUND_SERVICE_TYPE_SPECIAL_USE 权限声明),SPECIAL_USE 不可用时降级 DATA_SYNC


10. 与旧版的主要差异(落地理清)

  1. 会话引擎:移除 Termux/PTY,改为自包含 QuroShellSession(管道 stdin + 哨兵协议 + 可选 VT 渲染);
  2. 会话统一管理:新增 QuroTerminalSessionManager 持有默认共享会话,AI/UI/CMS 共用;
  3. 宿主 homegetExternalFilesDir/sandbox-homefilesDir/linux-sandbox/sandbox-home
  4. DNS:从单一 /etc/hosts 升级为三层(hosts 静态 + 应用层 DoH + dnscrypt-proxy 自动选最优上游);
  5. 保活QuroTerminalKeepAliveService 进程内 fork shell,服务存活 = 子进程存活;
  6. ACIQuroTerminalAciService 12 能力 + 审计日志 + Token/权限校验;
  7. 四路 IPC:ContentProvider / DeepLink / Intent / Broadcast 统一落到 Controller + SessionManager
  8. 类与文件名延续去品牌化约定(Quro* 包名 com.ai.assistance.quro),内部路径 QuroAI_logs/QuroDocs/ 等 B 类标识保持不变。

本文档由源码(v1.0.72)实际结构整理,关键类名/方法名/常量均可在 app/src/main/java/com/ai/assistance/quro/ 下核对。


11. 快速开始

11.1 典型使用场景

  • AI 工具层:通过 terminal_exec 在对话中直接执行 Linux 命令、安装依赖、读写文件;
  • 终端界面:打开 Nova 终端,获得一个完整的 Ubuntu 24.04 交互式 shell;
  • CMS 开发环境:在开发环境面板安装 Python / Node / Go 等运行时,并复用同一共享会话。

11.2 首次启动流程

  1. 打开应用,进入「终端」或「开发环境」页面;
  2. 若尚未安装 Linux 环境,点击顶栏「安装 Linux 环境」或发送 linux:install
  3. 等待 rootfs 下载与解压(秒级~分钟级,视网络而定);
  4. 安装完成后自动进入 LINUX 模式,即可使用 apt / python3 / node 等命令。

提示:安装过程走 DNS 三层方案,若 apt 更新超时,请先确认设备可联网且 dnscrypt-proxy 已就绪(见 FAQ Q3)。


12. 构建与运行

12.1 环境要求

  • Android Studio(推荐最新稳定版);
  • JDK 17+;
  • Android SDK(minSdk / targetSdk 以 app/build.gradle.kts 为准)。

12.2 构建步骤

# 克隆项目后,在项目根目录执行
./gradlew :app:assembleDebug

产物路径:app/build/outputs/apk/debug/app-debug.apk

12.3 安装与验证

adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n com.ai.assistance.quro/.MainActivity

打开终端后输入 uname -a,若输出包含 Linux ... aarch64 且可执行 apt,说明 LINUX 模式已生效。


13. 常见问题补充

Q7:如何导出终端日志?

A:在终端内执行 exportLog() 对应命令,或通过 UI 的导出入口,日志将写入 Documents/QuroDocs/terminal_<ts>.log

Q8:如何切换 / 销毁会话?

A:通过 QuroTerminalSessionManagerswitchDefault(id) / destroySession(id);在 UI 上对应会话列表的切换与关闭操作。

Q9:如何申请「所有文件访问」权限?

A:在系统设置中为应用开启「所有文件访问」(MANAGE_EXTERNAL_STORAGE)。开启后 sharedStorageHostDir 才会返回 /sdcard 并真正 bind 到容器。

Q10:前台服务通知可以关闭吗?

A:specialUse 前台服务会常驻通知栏(NOTIF_ID=9529 / 9530),这是 Android 保活机制的一部分,不建议关闭;关闭可能导致息屏 / 切后台时会话被系统回收。


14. 结语

Zorv AI 终端通过 proot + Ubuntu 24.04 用户空间统一共享会话DNS 三层兜底前台保活,在无 root 的 Android 设备上提供了一套稳定、可扩展的 Linux 命令行环境。配合 ACI 受控端与四路 IPC,终端能力可被 AI、UI、CMS 及外部控制端灵活复用。

本文档基于源码(v1.0.72)实际结构整理,关键类名 / 方法名 / 常量均可在 app/src/main/java/com/ai/assistance/quro/ 下核对。后续版本如有架构调整,请同步更新本文档。


15. 权限与依赖清单

15.1 运行时权限

权限用途备注
FOREGROUND_SERVICE前台保活服务Android 9+ 必需
FOREGROUND_SERVICE_SPECIAL_USEspecialUse 前台服务类型Android 14+ 必需
FOREGROUND_SERVICE_DATA_SYNCDATA_SYNC 降级类型备用
POST_NOTIFICATIONS前台服务通知Android 13+ 需动态申请
INTERNETrootfs 下载、DNS 解析、DoH 请求必需
ACCESS_NETWORK_STATE网络状态检测可选
MANAGE_EXTERNAL_STORAGE绑定 /sdcard 到容器可选,需用户授权
READ/WRITE_EXTERNAL_STORAGE旧版外部存储访问仅旧版 Android

15.2 依赖库与资源

  • prootlibproot.sonativeLibraryDir,缺失时从 assets 解压);
  • dnscrypt-proxyusr/sbin/dnscrypt-proxy(rootfs 内 apt 安装);
  • Termux terminal-emulator:仅旧 PTY 路径保留(QuroTerminalJNI.java);
  • Compose / Material3:终端 UI 与开发环境面板;
  • AIDL:ACI 服务(BaseAidlAciService)与 Binder 接入。

16. 数据持久化与状态恢复

16.1 持久化文件

路径内容说明
filesDir/linux-sandbox/rootfs/Ubuntu 24.04 rootfs网络下载 gz 解压
filesDir/linux-sandbox/sandbox-home/容器内 /rootapp 内部存储,支持 symlink
filesDir/linux-sandbox/tmp/临时文件容器内 /tmp
filesDir/quro_terminal_sessions.json会话元数据进程重启后 alive=false
Documents/QuroDocs/terminal_<ts>.log终端滚动缓冲导出用户主动导出

16.2 状态恢复流程

  1. 应用启动 → QuroTerminalSessionManager.load(ctx) 载入会话元数据;
  2. 前台保活服务 startLoopensureSessionSafe(installIfMissing=false) 重建死亡会话;
  3. 用户打开终端 → ensureDefault(installIfMissing=true) 跟随安装并恢复默认共享会话;
  4. 每次命令前 prepareRuntimeExtras 刷新 resolv.confgetprop 垫片,避免快照过期。

17. 安全与隐私说明

17.1 权限校验

  • ACI 服务onCheckPermission 仅放行自身包名与 com.ai.assistance.quro(ZorvAI 控制端);
  • ContentProvider:按 Binder.getCallingUid() 校验 READ_TERMINAL / WRITE_TERMINAL
  • BroadcastSEND_TERMINAL_BROADCAST / RECEIVE_TERMINAL_BROADCAST 权限保护;
  • DeepLink / Intent:显式 Intent 绑组件,隐式仅 Activity/Broadcast。

17.2 Token 校验

  • onVerifyToken:自身调用跳过,否则走 AciTokenVerifier.verify
  • 外部控制端需携带有效 Token 才能调用 ACI 能力。

17.3 审计日志

  • 每次 ACI 调用 recordAudit(seq/时间戳/调用方/能力/耗时);
  • 最近 1000 条,CopyOnWriteArrayList 线程安全;
  • 可通过 get_audit_log 能力查询。

17.4 数据隔离

  • rootfs 与 sandbox-home 均位于 app 内部存储,其他应用默认不可访问;
  • 仅当用户授予「所有文件访问」权限时才绑定 /sdcard,且「挂载即真实可用」。

18. 性能与资源占用

18.1 proot 沙箱

  • CPU:proot 通过系统调用翻译模拟 Linux 环境,CPU 开销集中在系统调用密集场景(如 apt、编译);
  • 内存:rootfs 常驻内存约 100–300MB(视已安装包数量);
  • 磁盘:rootfs 初始约 300–500MB,随安装包增长。

18.2 前台保活服务

  • PERIOD_MS=15_000ms 巡检一次,开销极低;
  • 进程内 fork shell 子进程,服务存活 = 子进程存活。

18.3 DNS 代理

  • dnscrypt-proxy 常驻监听 127.0.0.1:53,内存占用约 10–20MB;
  • 每次命令前 prepareRuntimeExtras 重跑,被回收自动重启。

18.4 优化建议

  • 避免在容器内运行高 CPU 密集任务(如大型编译)以节省电量;
  • 定期清理 sandbox-home 中不再使用的缓存文件;
  • 若磁盘紧张,可删除 rootfs 中未使用的语言运行时。

19. 故障排查与自愈机制

19.1 自愈策略

场景自愈机制
proot 启动失败降级 createDevice(DEVICE /system/bin/sh),终端永远可用
会话死亡前台保活服务 15s 巡检重建
DNS 解析失败三层逐级兜底:hosts 静态 → DoH 预解析 → dnscrypt-proxy
dnscrypt-proxy 被回收下条命令前 prepareRuntimeExtras 自动重启
前台服务 SPECIAL_USE 失败降级 DATA_SYNC
rootfs 下载中断断点续传 / 重新下载(视实现)

19.2 常见异常与排查

Q11:容器内 apt 安装包时提示「无法解析域名」?

A:确认 DNS 三层已生效:cat /etc/resolv.conf 应包含 nameserver 127.0.0.1curl -sI https://dns.alidns.com 应可达。若代理未启动,检查 dnscrypt-proxy 是否已安装。

Q12:容器内无法写入 /sdcard

A:确认已授予「所有文件访问」权限(MANAGE_EXTERNAL_STORAGE)。未授权时 sharedStorageHostDir 返回 null,/sdcard 不会绑定。

Q13:终端 UI 卡顿 / 渲染异常?

A:确认 VT 引擎已启用(vt 非 null)。若使用旧 Termux PTY 路径,注意 mRenderer.mFontWidth 空指针崩溃问题,建议切换到新默认引擎。

Q14:前台服务通知频繁出现?

A:specialUse 前台服务会常驻通知栏,这是 Android 保活机制的一部分。若频繁重启,检查系统是否限制后台启动,或尝试关闭电池优化。


20. 扩展开发指南

20.1 新增 ACI 能力

  1. QuroTerminalAciService.onCreateCapabilities 添加 Capability.create(...)
  2. onCallwhen 分支添加 handleXxx
  3. 审计日志自动记录,无需额外处理。

20.2 新增 IPC 路径

  1. QuroTerminalController / QuroTerminalSessionManager 实现统一语义;
  2. 新增 ContentProvider / DeepLink / Intent / Broadcast 处理器;
  3. AndroidManifest.xml 声明组件与权限。

20.3 接入新语言运行时

  1. QuroDevEnvScreen 添加运行时面板;
  2. 复用默认共享会话与 QuroLinuxEnv 后端;
  3. 在 rootfs 内通过 apt 或源码安装运行时。

20.4 自定义 apt 镜像

  1. curatedDevHosts 添加镜像主机;
  2. UBUNTU_APT_MIRRORS 配置镜像地址;
  3. 层 1/2 会自动预解析镜像主机 IP。

21. 结语(补充)

本文档已覆盖 Zorv AI 终端的架构设计、核心组件、Linux 沙箱、保活机制、ACI 受控端、IPC 接入、快速开始、构建运行、FAQ、权限依赖、数据持久化、安全隐私、性能优化、故障自愈与扩展开发等完整内容。无论是使用者、集成方还是二次开发者,都能据此快速上手与排查问题。

后续版本如有架构调整或新增能力,请同步更新本文档,保持与源码(v1.0.72)一致。