文档版本:v1.0.72(对应
app/build.gradle.kts的versionName/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. 概述
- 2. 整体架构
- 3. 核心组件
- 4. Linux 沙箱(proot + Ubuntu 24.04)
- 5. 前台保活(QuroTerminalKeepAliveService)
- 6. ACI 受控端(QuroTerminalAciService)
- 7. 命令路由与四路 IPC(外部接入)
- 8. 终端内 ACI 命令
- 9. 开发指南与 FAQ
- 10. 与旧版的主要差异(落地理清)
1. 概述
Zorv AI 终端是一套无 root、无 Shizuku 的 Android 内置命令行环境,核心能力是:
- 在 Android 上跑起一个完整的 Ubuntu 24.04 (ARM64) 用户空间(proot 沙箱),从而获得
python3、apt、node、pip、npm、任意写文件等 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 等 | 同名延续;新增 QuroTerminalSessionManager、QuroTerminalSentinel、QuroHostBridge |
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):非交互式一次性执行(AIterminal_exec设备回退用),与活动会话解耦;Linux 环境就绪时走 proot(runCommandInLinux),否则回退/system/bin/sh(runCommandInDevice);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 按行读入 ComposeSnapshotStateList滚动缓冲区; -
为何不用 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-9:
interrupt()两阶段——先写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.java(libtermux-terminal的createSubprocess/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)
resolveHostIpsByDoH 对 curatedDevHosts 逐主机走 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=0x02,props uint16 LE=0,Addr留空,BootstrapIP内嵌解析器 IP),使 proxy 直连解析器 IP 并以providerName作 SNI,不依赖被阻断的系统 DNS 引导; -
measureDohLatency(bootstrapIp):测:443TCP 建连延迟(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 : Service(specialUse 前台服务,CHANNEL_ID=quro_terminal_channel,NOTIF_ID=9529):
onCreate:startForeground(NOTIF_ID, ..., SPECIAL_USE),SPECIAL_USE 失败降级DATA_SYNC;startLoop:立即ensureSessionSafe(installIfMissing=false)(自启动不下载)+ 拉起 ACI 服务;每PERIOD_MS=15_000ms巡检:会话死亡重建、ACI 服务在跑;- 在本服务进程内 fork shell 子进程(
heldSession),服务存活 = shell 子进程存活,息屏/切 App/后台不被杀; onStartCommand:ACTION_STOP→stopSelf();系统意外重建时确保巡检循环仍运行(START_STICKY);onDestroy:loopJob.cancel()+heldSession?.destroy()。
6. ACI 受控端(QuroTerminalAciService)
QuroTerminalAciService : BaseAidlAciService(前台服务 specialUse,NOTIF_ID=9530)—— 继承 ACI 基类,注册 12 个能力:
| # | 能力 | 说明 |
|---|---|---|
| 1 | exec | 执行命令,返回 exit_code/output/error/timed_out(FLAG_BACKGROUND,默认超时 14s,交互 30s) |
| 2 | create_session | 创建新会话,返回 session_id |
| 3 | destroy_session | 销毁指定会话 |
| 4 | send_input | 向会话发原始输入(交互式) |
| 5 | get_session_status | 会话实时状态(alive/busy/cwd/last_exit) |
| 6 | list_sessions | 列出所有会话 |
| 7 | set_session_env | 设置环境变量(当前仅创建时生效,运行时受限) |
| 8 | get_session_env | 会话环境概要 |
| 9 | list_capabilities | 列出所有能力 |
| 10 | get_service_status | 服务运行状态(pid/sessions_count/uptime/version) |
| 11 | get_audit_log | 调用审计日志(最近 1000 条,CopyOnWriteArrayList) |
| 12 | help | 帮助信息 |
- 权限:
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 | 权限 |
|---|---|---|---|
| ContentProvider | TerminalProvider | content://com.ai.assistance.quro.terminal/{sessions|sessions/{id}|sessions/{id}/output|exec|status|capabilities} | READ_TERMINAL / WRITE_TERMINAL(按 Binder.getCallingUid() 校验) |
| DeepLink | TerminalDeepLinkHandler | quro://terminal/{exec|sessions|status|create}?cmd=...&timeout=... | 同应用内 |
| Intent | TerminalIntentHandler | com.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 |
| Broadcast | TerminalBroadcastReceiver | 同上 Action(有序广播 sendOrderedBroadcast 支持结果传播) | SEND_TERMINAL_BROADCAST / RECEIVE_TERMINAL_BROADCAST |
四路统一语义:exec 经 QuroTerminalController.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:install;runCommand/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.onCreateCapabilities 加 Capability.create(...) 并在 onCall 的 when 加分支;审计日志自动记录。
Q6:前台服务类型?
A:specialUse(Android 14+ 需要 FOREGROUND_SERVICE_TYPE_SPECIAL_USE 权限声明),SPECIAL_USE 不可用时降级 DATA_SYNC。
10. 与旧版的主要差异(落地理清)
- 会话引擎:移除 Termux/PTY,改为自包含
QuroShellSession(管道 stdin + 哨兵协议 + 可选 VT 渲染); - 会话统一管理:新增
QuroTerminalSessionManager持有默认共享会话,AI/UI/CMS 共用; - 宿主 home:
getExternalFilesDir/sandbox-home→filesDir/linux-sandbox/sandbox-home; - DNS:从单一
/etc/hosts升级为三层(hosts 静态 + 应用层 DoH +dnscrypt-proxy自动选最优上游); - 保活:
QuroTerminalKeepAliveService进程内 fork shell,服务存活 = 子进程存活; - ACI:
QuroTerminalAciService12 能力 + 审计日志 + Token/权限校验; - 四路 IPC:ContentProvider / DeepLink / Intent / Broadcast 统一落到
Controller+SessionManager; - 类与文件名延续去品牌化约定(
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 首次启动流程
- 打开应用,进入「终端」或「开发环境」页面;
- 若尚未安装 Linux 环境,点击顶栏「安装 Linux 环境」或发送
linux:install; - 等待 rootfs 下载与解压(秒级~分钟级,视网络而定);
- 安装完成后自动进入 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:通过 QuroTerminalSessionManager 的 switchDefault(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_USE | specialUse 前台服务类型 | Android 14+ 必需 |
FOREGROUND_SERVICE_DATA_SYNC | DATA_SYNC 降级类型 | 备用 |
POST_NOTIFICATIONS | 前台服务通知 | Android 13+ 需动态申请 |
INTERNET | rootfs 下载、DNS 解析、DoH 请求 | 必需 |
ACCESS_NETWORK_STATE | 网络状态检测 | 可选 |
MANAGE_EXTERNAL_STORAGE | 绑定 /sdcard 到容器 | 可选,需用户授权 |
READ/WRITE_EXTERNAL_STORAGE | 旧版外部存储访问 | 仅旧版 Android |
15.2 依赖库与资源
- proot:
libproot.so(nativeLibraryDir,缺失时从 assets 解压); - dnscrypt-proxy:
usr/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/ | 容器内 /root | app 内部存储,支持 symlink |
filesDir/linux-sandbox/tmp/ | 临时文件 | 容器内 /tmp |
filesDir/quro_terminal_sessions.json | 会话元数据 | 进程重启后 alive=false |
Documents/QuroDocs/terminal_<ts>.log | 终端滚动缓冲导出 | 用户主动导出 |
16.2 状态恢复流程
- 应用启动 →
QuroTerminalSessionManager.load(ctx)载入会话元数据; - 前台保活服务
startLoop→ensureSessionSafe(installIfMissing=false)重建死亡会话; - 用户打开终端 →
ensureDefault(installIfMissing=true)跟随安装并恢复默认共享会话; - 每次命令前
prepareRuntimeExtras刷新resolv.conf与getprop垫片,避免快照过期。
17. 安全与隐私说明
17.1 权限校验
- ACI 服务:
onCheckPermission仅放行自身包名与com.ai.assistance.quro(ZorvAI 控制端); - ContentProvider:按
Binder.getCallingUid()校验READ_TERMINAL/WRITE_TERMINAL; - Broadcast:
SEND_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.1;curl -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 能力
- 在
QuroTerminalAciService.onCreateCapabilities添加Capability.create(...); - 在
onCall的when分支添加handleXxx; - 审计日志自动记录,无需额外处理。
20.2 新增 IPC 路径
- 在
QuroTerminalController/QuroTerminalSessionManager实现统一语义; - 新增 ContentProvider / DeepLink / Intent / Broadcast 处理器;
- 在
AndroidManifest.xml声明组件与权限。
20.3 接入新语言运行时
- 在
QuroDevEnvScreen添加运行时面板; - 复用默认共享会话与
QuroLinuxEnv后端; - 在 rootfs 内通过
apt或源码安装运行时。
20.4 自定义 apt 镜像
- 在
curatedDevHosts添加镜像主机; - 在
UBUNTU_APT_MIRRORS配置镜像地址; - 层 1/2 会自动预解析镜像主机 IP。
21. 结语(补充)
本文档已覆盖 Zorv AI 终端的架构设计、核心组件、Linux 沙箱、保活机制、ACI 受控端、IPC 接入、快速开始、构建运行、FAQ、权限依赖、数据持久化、安全隐私、性能优化、故障自愈与扩展开发等完整内容。无论是使用者、集成方还是二次开发者,都能据此快速上手与排查问题。
后续版本如有架构调整或新增能力,请同步更新本文档,保持与源码(v1.0.72)一致。