Repository navigation
DeepSeek Harness Android v0.2.10(正式版)
变更内容
上一个版本:v0.2.4-mobile-335
- docs(0.2.10): 补上真机回执(小窗出画面与切档、白名单拼音搜索通过;残留删除仍 ACCESS_DENIED;壳页会话列表本机读不出)
- release(0.2.10): 版本号升到 0.2.10(versionCode 32),并新增本版发布说明
- docs(license): 新增「许可义务 ↔ APK 实物」对照表(含取证方法、26 条清单与逐项一致性读数)
- feat(mobile): 副屏操作、无障碍自动化规则引擎、壳页会话列表与存储清理一轮落地
- 修复插件市场皮肤资源加载
- docs(0.2.9): 补上 0.2.9 真机回执,并如实记录两项没取到的读数
- 修复运行时升级后的插件状态恢复
- fix(overlay): 悬浮球闪退根因修复,短按改弹二级菜单并支持副屏档位
- 优化设置分类与会话显示空间
- fix(runtime): 清理失败按类型细分,残留改用不跟随符号链接的兜底删除器
- fix(runtime): 旧运行时残留不再阻塞安装,并记录清理失败的原因
- fix(virtualscreen): 副屏会话不再因触摸手势的构造顺序启动失败
- docs: 补 0.2.5 的真机回执(删除 previous/ 抛 FILESYSTEM_ERROR,改名后安装继续)
- fix(runtime): 运行环境更新不再被清理失败卡死,并记录失败步骤
- feat(virtualscreen): 副屏预览节奏可选、触摸直传、目标切换与节点树
- docs: 补上「插件接回链接」的真机复测回执
- fix(plugins): 运行时升级后把保留区里的插件链接接回去,不再需要重装
- fix(virtualscreen): 副屏空白帧不再进缓存,错误码按可重试语义区分
- feat: polish responsive frontend and overlay controls
安装与配置
- 安装 app-release.apk(离线 APK,已内置 PRoot 运行器 + Ubuntu rootfs)。
- 首次打开后点「安装并进入对话」;rootfs 从 APK 内置资产解压,无需联网下载。
- 断网也能安装;若提示「未内置运行时」请确认下载的是本 Release 的 APK。
资产
- app-release.apk(APK,含 PRoot 运行器)
- rootfs.bundle(Ubuntu 24.04 ARM64 + Node 24.19 + dsh 0.2.0-rc.2 + 官方 Harness 前端 Android 适配)
- runtime-manifest.json
- checksums.txt(以上三项资产的 SHA-256)
runtime-manifest.json SHA-256:d13d407bf71a1b8a2cbec9f67c92d314d78004f578561a767a4cac42bc5a0122
0.2.10 测试版:副屏小窗不再黑屏,副屏可被 AI 实时操作,无障碍规则引擎与壳页会话列表落地
基线是 0.2.9(versionCode 31)装机后的真机读数:
- 副屏小窗预览长时间全黑。点了「小窗」以后窗口确实出现(
dumpsys window里能看到本应用名下
appop=SYSTEM_ALERT_WINDOW的窗口),但画面一直是黑的;同一时刻用screencap -d <SurfaceFlinger 显示 id>
直接抓副屏却能拿到完整画面,说明副屏本身有画面、问题出在预览这条链上。用户的原话是
「实际不一样,我无法截图,并且是黑屏」。 - 0.2.9 那个安装在手机上的 release 包构建于更早的时间点,不含本轮任何改动;本轮的改动此前只存在于
源码与单元测试里。
改动
一、目标应用副屏(实验功能)
- AI 可实时操作副屏:工具面新增手势与点击/滑动(可在一次调用里给出多点路径与时长)、
启动目标应用(launch,按包名或组件名拉起)与跟随前台应用(follow,返回当前副屏最前面的
应用,已在前台时不重复拉起)。启动与跟随都排除本应用自身、桌面、输入法与系统界面。 - 文字输入回退链:
setText→ 聚焦后setText→ 剪贴板粘贴 →input text逐字符keyevent。
只有文字确实落进目标输入框后才会按需发送回车;单次输入上限 512 字符,超出会明确失败而不是截断。 - 三条真机解析修正(都是拿真机
dumpsys原文改的):cmd package resolve-activity --brief的输出里第一行是priority=… isDefault=…元信息,
解析时跳过含=的行、取最后一个组件样式的候选行,只有元信息行时按失败处理。- 桌面进程改成三级来源:当前
dumpsys结果 → 按需再查一次dumpsys activity→HOME意图兜底;
单条命令输出上限 3 MB,超限时落到意图兜底。 topResumedActivity=在每块显示器上各有一条,按Display #<id>分段后只绑定主屏(display 0)。
- 小窗黑屏修复(三条根因叠加):
- 小窗从来没有状态行 —— 预览状态回调在悬浮窗里没有接线,黑屏时用户看不到任何原因;
- 空白帧判定过去只由宿主按固定「3 行 × ≤48 点、通道极差 ≤8」判断,真机大片白底的页面会被
整帧误判成空白,于是一帧都不上屏; - 采集侧用
runCatching静默吞异常,采集一出不来帧,客户端只能一直保持旧状态。
修法是把判定权威挪到客户端:在解码后的位图上取 96×128 亮度网格(列采样 128、行采样 96、
内容阈值 24、帧间差阈值 12),空白帧一律不上屏、也不清屏(保留最近一张有内容的画面),
只有会话失效才清屏;小窗补上状态行;采集失败时把异常类名记进诊断字段。
- 小窗不再加
FLAG_SECURE:过去它与全屏查看器一样禁止截屏,结果是用户自己截图、系统录屏和
adb screencap全都失败(上一版就因此无法自查)。小窗现在按普通悬浮窗处理,全屏查看器保留
FLAG_SECURE。 - 帧率档位(省电 / 15 / 30 / 60 FPS)在小窗与全屏查看器里都能切,当前档高亮;取帧间隔随档位
(省电 180 毫秒,其余三档下限 120 毫秒)。档位决定的是采集间隔:目标应用自己没有新画面时,
取到的仍是同一张图,这是 Android 虚拟显示的行为,不是应用在复用旧帧。
二、无障碍应用自动化(本版只判定,不执行)
- 规则引擎落地:规则模型、规则存储与规则匹配器,配套 71 项单元测试。要点:
选择器内部字段是严格 AND;命中多个选择器时取屏幕顺序最靠前的那个;click命中不可点击的
节点时降级为点击中心并标记degradedToCenter;上限由maxActions单独把关,冷却只决定配额
何时重开;取不到 Activity 时按ACTIVITY_UNKNOWN拒绝放行;坏数据只自愈单个包,不影响其它包。 - 参数改名:原先四个与第三方工具重名的键改为本项目自定命名 ——
allowActivities、denyActivities、
maxActions、resetOn。旧键不再接受、也不做迁移(该功能从未发布,设备上不存在真实规则)。
同时清掉了文档与注释里的第三方项目指涉;许可义务文本一个字节未动。 - 白名单应用搜索:支持拼音与多关键词(例如
wx、weixin都能搜到微信),全角空格、软连字符、
零宽字符、双向控制符会先归一化;筛选过程不再出现「永远在读取」的循环刷新。 - 免责条款:设备 Shell 页面写明无障碍能力的边界 —— 服务不做后台监听,也不批量上传界面;
只有用户让 AI 执行操作时,读取到的界面节点才会按需进入当前会话与所选模型服务。
三、壳页会话列表
宿主经既有的「运行访客脚本」通道新增会话列表读取:只返回 id、title、updatedAt,不读会话正文、
不返回片段;单个日志文件的扫描上限为 1 MiB 且为有界扫描,回退标题只在「日志完整扫完且一条标题
事件都没有」时才使用。读不到时如实返回不可用并让界面写「读不到会话列表,这不代表没有会话」,
不假装成空列表。
四、存储与运行时
- 兜底清扫器:严格删除器失败时按「严格 → 兜底 → 记失败」的顺序处理运行时残留,逐项记录
类别、份数与字节数(不记路径与文件名),永不抛异常。 - 版本管理页残留卡片:显示残留份数与体积、无残留时按钮置灰、清理前二次确认、清理后重新查询
真实剩余量并如实回报(例如「已回收 3 份(其中 1 份走了兜底删除器),2 份未能删除」)。 - 失败码细分:运行时更新后的残留清理会把失败原因细分成
ACCESS_DENIED、NOT_EMPTY、
NO_SUCH_FILE、NOT_A_DIR等子类,取代过去笼统的FILESYSTEM,便于真机定位。
五、外观与图标
- 启动图标按反馈铺满:传统图标取到 97%/100% 画布,自适应图标前景贴到安全区上限(约 61%),
并补上roundIcon;悬浮球球面改用drawable-nodpi位图 +fitXY,不再被系统留白。 - 设置分类芯片与卡片按主题色/透明度/模糊统一取色,新增
--bg-scrim、--shadow-color令牌;
无障碍与副屏的浮层颜色改用主题调色板,深色与浅色下都不再出现写死的硬编码色值。
六、契约测试修复
插件市场只读资源白名单改造后,WebView 允许分支跨行、旧的单行正则失配,CI 的脚本测试因此失败。
现在改为跨行断言,并新增一条「插件市场白名单必须严格落在 dsh-market.com(端口 443、无用户信息、
仅 GET/HEAD/OPTIONS 且非主框架)」的契约测试。
- 版本号 0.2.9 → 0.2.10(
versionCode 32)。 - 上一版内容见 发布说明-0.2.9.md。
未解决的设备限制
- 副屏需要 Shizuku 授权与 Android 10 及以上。触摸直传(
touchChannel=stream)依赖隐藏接口
InputManager.injectInputEvent,被 ROM 拦掉时回退到离散的input -d <显示编号>。 - 节点树读取与中文文字注入要求用户手动开启本应用的无障碍服务;锁屏、系统设置、涉及权限、支付、
验证码与密码的页面不在自动化范围内。 - 无障碍自动化规则这一版只判定、不执行:执行器接线(事件订阅、四层节流、指纹去重)与桥命令、
AI 工具面、设置页规则 UI 都留到后续版本。 - Shizuku 不是 root,也不提供永久保活;应用被系统强制停止后,旧的 Harness 会话与临时凭据可能无法
恢复,界面会提示需要重新连接。 - 不宣称可以绝对防止 Android 或厂商系统杀死进程。
- 运行时残留(
stale-*)在这台真机上删不掉:严格删除器与兜底删除器都拿到
java.nio.file.AccessDeniedException(诊断记code=ACCESS_DENIED),因此运行时目录会随每次
更新多留一份旧副本。根因(权限、属主还是 SELinux)未定案,本版只做到让残留可见、可诊断、可重试。 - 壳页会话列表在本机读不出会话:界面按设计显示「读不到会话列表…这不代表没有会话」,
但探针失败使该功能不可用,待定位。
验证
- Kotlin 单元测试:88 个测试类 / 780 个用例 / 0 失败 / 0 错误 / 22 跳过(
gradle :app:testDebugUnitTest)。 lintRelease:0 Error / 59 Warning(警告族与上一版相同,本版新增代码零条目)。- 前端:37 个测试文件 / 480 个用例全通过(
npx vitest run);tsc --noEmit与eslint .退出码均为 0。 - 脚本层:184 项 / 180 通过 / 0 失败 / 4 跳过(
node --test scripts/*.test.mjs;跳过的 4 项是
「Windows 缺 koffi 原生绑定」的跨进程写锁用例)。
真机回执(HONOR AAP-AN00,2026-10-05)
装机包来自 CI 产物(run 37291709323,dsh-harness-android-release,300 MB,团队密钥签名);
dumpsys package 读数 versionCode=32 / versionName=0.2.10,lastUpdateTime=2026-10-05 18:05:52。
随后在应用内更新运行环境:当前运行时 0.2.10-mobile-352(960 MB,dsh 0.2.0-rc.2),上一版本
0.2.9-mobile-345 保留,「随安装包内置」一栏同为 0.2.10-mobile-352。
已通过(真机实拍/读数)
- 副屏悬浮小窗不再黑屏:小窗内出现目标应用(Edge)的真实画面,窗口标题「目标应用 · 拖动」,
按钮为「展开 / 收起」,窗口内一行四档「省电 / 15 FPS / 30 FPS / 60 FPS」,状态行读数为
「副屏预览 · 点击或滑动操作 · 0.7 fps · 画面暂无变化 · 档位 省电」。 - 窗口内切档生效:点「30 FPS」后状态行变为「3.6 fps · 画面暂无变化 · 档位 30 FPS」。
- 小窗不再遮挡系统截图:小窗在屏时对内置屏
screencap成功(2,072,692 B);上一版此处会被
FLAG_SECURE挡掉并报「Capturing failed」。 - 「结束副屏」后虚拟显示被回收:
dumpsys display的mViewports只剩displayId=0。 - 白名单应用搜索:输入
wx命中「微信」(com.tencent.mm)并置顶,同时命中同音「无线紧急警报」;
输入wx mm只命中「微信」且计数为「当前显示 1 / 1」,多词 AND 与顺序无关成立;提示文案
「支持拼音与首字母:微信可搜 wx、weixin;多个词用空格分开,顺序随意。」在真机可见。
部分通过(能力可见,问题仍在)
- 运行时残留卡片可用:版本管理页「运行时占用」显示「可回收的残留:5 份,共 0 B」,与诊断日志里
5 条phase=cleanup|…|reason=stale_*一一对应;「清理残留」会弹出二次确认
(「确认清理运行时残留?…不影响会话、设置与当前运行时」)。 - 但残留没有被删掉:真机诊断摘要为
phase=cleanup|result=failed|reason=stale_residue|count=0|bytes=0|residual=true|code=ACCESS_DENIED,
逐条行也都是result=failed|code=ACCESS_DENIED。即严格删除器与新增的兜底删除器在这台设备上
都被拒(java.nio.file.AccessDeniedException),本次更新后份数仍为 5。⇒ 本版把残留变成
「可见、可诊断、可重试」,没有解决磁盘残留本身。
未通过
- 壳页会话列表在本机返回不可用:真机界面显示「读不到会话列表 / 暂时读不到会话列表;这不代表没有
会话。」(这是设计中的诚实分支,没有假装空态),但探针在本机失败,列表读不出会话 ⇒ 该功能在
真机上不可用,待定位(可能与本机运行时目录形态或会话存储路径有关)。
本轮未走查(不宣称)
- 触摸直传
touchChannel的实际取值、input -d <显示编号>兜底是否被 ROM 识别为滑动;本轮只
在虚拟显示上用input -d 38 tap验证过「注入能改变目标应用画面」。 - AI 实时操作的 gesture / launchUri / launchPackage / follow / 文字输入四条链的端到端。
- 无障碍规则引擎(本版只判定、不执行)与执行器相关的一切行为。