Skip to main content
Glama
huguangyu666

android_emulator

by huguangyu666

dsh-plugin-android-emulator

把 ZCode 官方内置插件 android-emulator(MIT,© Z.ai)移植到 DeepSeek Harness。

提供 23 个 Android 原生工具:Gradle 构建、模拟器/真机生命周期、APK 安装与启动、截图、日志、ADB 与 UI Automator 自动化;并附带 android-dev 技能、/android 斜杠命令与原生设置面板。

本插件是集成层:不修改上游一行代码。上游产物以 MIT 许可逐字节 vendor 在 vendor/zcode-android-emulator/,来源、版本与逐文件 SHA-256 见 PROVENANCE.md。


1. 移植可行性结论

可以移植,而且属于「低风险高保真」移植。

原因:ZCode 的插件模型与 DSH 在关键处天然对齐 —— 上游插件本身就是一个 自包含的 stdio MCP 服务器(esbuild 打包,只 import node:* 内置模块), 而 DSH 有官方 MCP 桥接器 @deepseek-ai/dsh-mcp-client。两者对接后:

  • 工具名完全一致:上游 .mcp.json 里服务器名是 android-emulator,ZCode 会规范化成 android_emulator,模型看到 mcp__android_emulator__<tool>;本插件直接把 serverName 设为 android_emulator,产出的工具名与 ZCode 逐字节相同, 因此上游技能文本无需改写工具名。

  • 上游的 23 个工具、12 项 preflight 诊断、Gradle/ADB/UI Automator 全部逻辑 零改动复用,只是换了个宿主。

映射表

ZCode 机制

DSH 对应实现

.mcp.json 里的 ${ZCODE_PLUGIN_ROOT}

本包内 vendor/zcode-android-emulator/ 路径

${ZCODE_PROJECT_DIR}(子进程 cwd)

projectDir 设置,默认 DSH 进程工作目录

${ZCODE_PLUGIN_DATA}

<DSH_HOME>/android-emulator

userConfig.*(7 项)

ctx.settings.register('android-emulator', ...) 原生设置面板

skills/android-dev/SKILL.md

ctx.skills.registerProvider()(source: bundled, rank: 600)

commands/android-dev.md → /android-dev <goal>

DSH 原生技能手势:输入框打 /android-dev <goal>,技能正文作为 instructions 注入、你的话作为任务(已实测,见 §7)

(本移植新增)

ctx.tools.register() → android_set_project_dir:让模型自己把 MCP 工程根目录对齐到当前会话(ZCode 靠动态 cwd 自动完成,DSH 的 cwd 是静态的,必须补这一环)

(本移植新增)

ctx.commands.register() → /android:给人用的状态查看 / 切换工程根目录

插件被 ZCode 发现并注入系统提示

ctx.systemPrompt.section() 注入工作流段落

官方 MCP 服务器托管

@deepseek-ai/dsh-mcp-client stdio 传输 + 自动重连

ZCode 的 /android-dev 命令在 DSH 里不需要用 ctx.commands 复刻:dsh-commands 的 handler 只能返回给用户看的文本,没有「把任务派给模型」的通道。而 DSH 的技能手势 (/(^|\s)\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\s|$)/)对 userInvocable 技能做的事, 恰好等价于 ZCode 命令的 skills: frontmatter + $ARGUMENTS:技能正文进 instructions, 用户原话作为任务。所以这条是宿主原生覆盖,不是缺口。

与 ZCode 的差异

项

ZCode

本移植

工具名

mcp__android_emulator__*

完全相同

工具超时

60 s

默认 900 s(Gradle 首次构建很慢)

工程根目录

随会话工作区

进程级固定,用 /android dir <path> 切换(见「已知限制」)

技能资源

skills/android-dev/INSTALL_ENVIRONMENT.md

同样随包提供,resourceBase 指向 assets/android-dev/

宿主运行时

ZCode 内嵌 Node 24

DSH 的 Electron-as-Node 24.18,自动加 ELECTRON_RUN_AS_NODE=1


Related MCP server: AVD MCP Server

2. 工具清单(23 个)

全部以 mcp__android_emulator__ 为前缀,例如 mcp__android_emulator__android_preflight。

分组

工具

诊断

android_preflight

工程

android_discover_project、android_create_app、android_build_app、android_build_and_run

设备

android_list_devices、android_list_avds、android_start_emulator、android_stop_emulator、android_create_avd

应用

android_install_app、android_launch_app、android_terminate_app、android_open_url

观测

android_screenshot、android_logs

UI 自动化

android_ui_status、android_ui_describe、android_ui_resolve、android_ui_tap、android_ui_swipe、android_ui_type_text、android_ui_keyevent

android_preflight 会实际探测 12 项:Host OS、Android SDK root、插件默认值、adb、emulator、 sdkmanager、avdmanager、Java、Gradle、模拟器加速、AVD 列表、已连接设备。


3. 环境要求

  • DSH ≥ 0.1.5-rc.1(本插件在 0.1.7-rc.2 上验证)

  • Node ≥ 24(上游产物以 --target=node24 构建)。DSH 桌面版自带 Node 24.18,无需额外安装; 若宿主 Node 低于 24,插件会自动优先使用 PATH 上满足要求的 node。

  • macOS 或 Windows(上游 P0 明确不支持 Linux,android_preflight 会如实报告)

  • Android SDK:platform-tools(adb)、emulator、cmdline-tools(sdkmanager/avdmanager)

  • 模拟器工作流:至少一个 AVD;真机工作流:开启 USB 调试的设备

  • 可选:gradle 在 PATH 上(用于给缺 wrapper 的工程生成 Gradle wrapper)


4. 安装

方式一:官方插件安装器

dsh plugin --profile desktop add dsh-plugin-android-emulator

方式二:本地开发安装(推荐先 dry-run)

cd dsh-plugin-android-emulator

# 1. 还原上游产物(首次 clone 后必须执行;从本机 ZCode 安装目录复制并校验哈希)
node tools/vendor-upstream.mjs

# 2. 构建宿主产物
npm run build

# 3. 看看将要做什么改动
node tools/install-into-profile.mjs --profile desktop --dry-run

# 4. 真正安装(自动备份 profile package.json,并用 dsh --dump-config 复核合成树)
node tools/install-into-profile.mjs --profile desktop

安装后下次启动 DSH 生效(正在运行的 Host 不会热加载 profile bundles)。 卸载:node tools/install-into-profile.mjs --profile desktop --uninstall。

方式三:只做一次试验(不改任何 profile)

dsh --profile desktop --patch ./cordis.patch.yml headless "调用 android_preflight 看看环境"

5. 设置项(DSH 设置页 →「Android 模拟器」)

设置

默认

说明

enabled

true

是否启用

projectDir

''

MCP 工程根目录;留空 = DSH 进程工作目录

nodePath

''

运行 MCP 服务器的 Node;留空自动选择

sdkPath

''

Android SDK 根目录;留空自动探测 ANDROID_HOME / ANDROID_SDK_ROOT / 常见路径

defaultAvd

medium_phone

首选 AVD

apiLevel

35

建工程 / 装 SDK 包 / 选系统镜像用的 API level

buildToolsVersion

35.0.0

build-tools 版本(用于安装指引)

systemImageVariant

default

default / google_apis / google_apis_playstore

systemImageAbi

''

留空时 ARM64 主机用 arm64-v8a,其余 x86_64

jdkMajor

17

preflight 与安装指引使用的 JDK 主版本

toolCallTimeoutMs

900000

单次工具调用超时(15 分钟)

failOnStartupError

false

首次连接失败是否让插件加载失败

registerSkill

true

是否注册 android-dev 技能

promptSection

true

是否注入工作流系统提示段落

以上 7 项 Android 参数会以 ANDROID_PLUGIN_* 环境变量传给 MCP 服务器, 与上游 plugin.json 的 userConfig 一一对应。


6. 使用

斜杠命令(给人用)

/android                 # 查看状态:服务器路径、工程根目录、数据目录、Node 运行时、ANDROID_PLUGIN_* 环境
/android dir <path>      # 切换工程根目录(自动重启 MCP 服务器,23 个工具重新注册)

原生工具 android_set_project_dir(给模型用)

ZCode 的工程根目录跟着会话自动变(ZCODE_PROJECT_DIR = context.workingDirectory,每次连接动态解析); DSH 官方 MCP 桥接器的 cwd 只接受静态字符串,所以本移植补了一个原生工具让模型自己对齐:

android_set_project_dir            # 不传参数 = 用当前会话的工作目录
android_set_project_dir { path }   # 显式指定工程根目录

它做三件事:校验目录 → 按新根目录重建 MCP 桥接 → 在本地探测 settings.gradle(.kts) / gradlew / gradle.properties 等标记并直接返回,所以模型一次调用就能确认对不对, 不用再多跑一次 android_discover_project。返回形如:

{
  "ok": true, "changed": true,
  "previousProjectDir": "...", "projectDir": "C:\\…\\<你的安卓工程>",
  "source": "argument",
  "looksLikeAndroidProject": true,
  "markers": ["settings.gradle.kts", "gradlew", "gradlew.bat", "gradle.properties", "local.properties"]
}

系统提示词里也写了「若 android_discover_project 找不到 Gradle 根,就调 android_set_project_dir」, 所以这一步是自愈的,不需要人插手。isConcurrencySafe() 返回 false —— 重挂是进程级操作,不能并发。

技能

android-dev 技能会出现在会话技能目录里,模型可直接加载;其工作流为: android_preflight → android_discover_project(或 android_create_app)→ android_build_and_run → android_screenshot → 按需 android_logs / UI 自动化。

在输入框直接打 /android-dev <你的目标> 即可启动开发循环 —— 这是 DSH 的原生技能手势, 等价于 ZCode 的 /android-dev 命令(技能正文进 instructions,你的话作为任务):

/android-dev 帮我做一个计数器 App,跑起来截图给我看

技能还随包提供上游的 INSTALL_ENVIRONMENT.md(macOS / Windows 的 JDK、Gradle、 cmdline-tools、SDK 包、AVD 创建、模拟器加速逐步指引)。

本机已有 DSH 自带的 Android 技能(adaptive、agp-9-upgrade、edge-to-edge、 navigation-3、camerax、testing-setup、r8-analyzer 等)。那些覆盖怎么写, 本插件覆盖怎么跑起来并验证,两者互补。


7. 验证

npm run verify          # 构建 + 全部测试
npm run vendor:check    # 校验 vendor 目录与上游逐文件哈希一致

测试金字塔(45 个用例,全部通过):

层

文件

覆盖内容

L1 契约

test/contract.test.mjs

files 含 cordis.patch.yml/vendor/assets;patch 只有一个 insert;无 export default;无运行时依赖;产物未内联 @deepseek-ai/*

L1 完整性

test/vendor-integrity.test.mjs

PROVENANCE 清单与 41 个 vendored 文件 SHA-256 逐一比对;bundle 只依赖 node:*

L2 生命周期

test/mock-lifecycle.test.mjs

用真实 schemastery 与真实 MCP 桥接模块 + 假 ctx,断言桥接配置、技能 provider、提示词段落、/android 行为、effect 作用域回收、缺服务降级

L3 端到端

test/mcp-server.test.mjs

真实 spawn 上游 MCP 服务器:握手、23 个工具与入参 schema、真实 android_preflight 报告、ANDROID_PLUGIN_* 覆盖生效、cwd 决定工程根

本机实测记录(Windows + 完整 Android SDK)

① 用 DSH 自己的运行时(Electron 44 / Node 24.18.1,ELECTRON_RUN_AS_NODE=1) 拉起上游 MCP 服务器:

initialize: {"name":"android-emulator","version":"0.1.0"} protocol 2024-11-05
tools: 23
names: mcp__android_emulator__android_preflight, ... , mcp__android_emulator__android_build_app
preflight ok: false | checks: 12
  PASS  Host OS / Android SDK root / Android plugin defaults
  PASS  adb / emulator / sdkmanager / avdmanager / Java
  PASS  Emulator acceleration / Android Virtual Devices
  FAIL  Gradle          <- 本机 PATH 无 gradle 且工程无 wrapper,符合预期
  FAIL  ADB devices     <- 当前没有运行中的设备,符合预期

② 真实 DSH 宿主端到端验收(隔离 profile,dsh --profile android-e2e "…"):

1. Yes — `android-dev` is in my skill catalog.
2. 23 `mcp__android_emulator__` tools.
3. android_preflight: passed 11, failed 2 — failed checks: Gradle (not found), ADB devices (0 devices).

即:技能注册、23 个工具注册、真实工具调用在真实 DSH 宿主里全部跑通, preflight 的 11/2 结果与直接探测一致(两项 FAIL 是真实环境状态,不是移植缺陷)。

③ 原生技能手势等价于 ZCode 的 /android-dev 命令(同 profile,直接以 /android-dev 开头):

$ dsh --profile android-e2e "/android-dev 我想做一个计数器 App,先别动手,只告诉我:按照技能里的
  默认工作流,你的第一步和第二步分别要调用哪个工具?"

按 `android-dev` 技能里的 Default Workflow,前两步是:
| 第 1 步 | mcp__android_emulator__android_preflight | 纯诊断…若环境缺失,先按 INSTALL_ENVIRONMENT.md 处理,
            不能替你接受 SDK 许可、输密码、清模拟器数据或删 AVD |
| 第 2 步 | mcp__android_emulator__android_discover_project | …并读取 warnings(缺 gradle.properties /
            local.properties / wrapper 要先修)|
补充:第 2 步里内含一个分支:如果发现没有 Android 工程,才在该步调用 android_create_app…
另外注意 path 解析:这两个工具的相对路径都相对插件的 projectDir(MCP project root),不是当前 shell 目录。

模型复述的「这两个工具的相对路径都相对插件的 projectDir(MCP project root), 不是当前 shell 目录」只存在于本移植改写后的技能正文里(上游 SKILL.md 里没有 projectDir 也没有 ambient shell directory 这句话)—— 证明注入的确实是这个技能。 /android-dev 手势完整可用,不需要再用 ctx.commands 复刻一条。

(注:同一次回答里提到的「不能替你接受 SDK 许可、清模拟器数据」是上游原文, 不能作为版本证据;projectDir 那句才是。)

④ 真实工程端到端(2026-09-25,本地一个真实工程 AiDocHelper v4.131 —— Kotlin + Compose + Room + OpenCV + MLKit,含 ABI splits):

步骤

结果

android_preflight

ok=true,12 项检查,仅 ADB devices 未就绪(尚无设备)

android_discover_project

root=""(= cwd)、modules=[:app]、appIds=[com.aidoc.helper]、4 个 APK

android_start_emulator

复用既有 AVD dj,5.5s 启动,serial=emulator-5554

android_build_app

:app:assembleDebug BUILD SUCCESSFUL in 22s(38 tasks up-to-date)

android_install_app

显式传 x86_64 APK,Success

android_launch_app

launched=true

android_screenshot

1080×2400 PNG,142,629 bytes,App 主界面渲染正常

android_logs

logcat 拿到 libopencv_java4.so ... ok / Library opencv_java4 loaded

android_stop_emulator

stopped=true

全程 85 秒(首次 12:23:54 → 12:25:19),未创建任何 AVD、未安装任何 SDK 包、 未修改工程任何源文件(git status 干净)。

两个实测细节值得记下:

  • 截图要等冷启动。 首轮在 launch 后 12 秒截图,拿到的是闪屏(39 KB); 该 App 冷启动要加载 OpenCV 原生库,45 秒后才是主界面(142 KB)。

  • ARM 转译让错配 APK 也能装上,详见 §8.5 —— 预测的 INSTALL_FAILED_NO_MATCHING_ABIS 没有发生,如实记录。

⑤ 在运行中的 DSH 会话里用插件自己的工具跑完整条链(2026-09-25 21:00, projectDir 指向同一工程,全部通过 MCP 工具调用,无脚本):

工具

结果

android_discover_project

root=""、modules=[:app]、appIds=[com.aidoc.helper]、4 APK、warnings=[]

android_create_avd

首次不传 device → 96M/320×640 废 AVD(见 §8.6);传 medium_phone 后 1536M/1080×2400 正常

android_start_emulator

serial=emulator-5554

android_build_app

BUILD SUCCESSFUL in 12s,38 tasks up-to-date;apkPath 又是 arm64(§8.5)

android_install_app

显式 x86_64 APK → Success

android_launch_app

launched=true,topResumedActivity=com.aidoc.helper/.MainActivity,crash buffer 为空

android_screenshot

1080×2400 PNG,141,507 bytes,主界面正常

android_stop_emulator

stopped=true

清理核对:临时 AVD dsh_tmp_test 已删、AVD 目录回到空、无残留 emulator/qemu-system 进程、无 adb 设备、工程 git status 0 改动。

这轮真实宿主验收抓到了一个只有实机启动才会暴露的 bug:Cordis 对未在 inject 声明的服务读取即抛错(cannot get property "skills" without inject), 所以 ctx.skills?.xxx 这种「防御式探测」根本防不住 —— ?. 之前就炸了。 正确做法是 ctx.inject(['skills'], inner => …)(服务可用时才执行) 与 ctx.get('skills')(不抛错的读取,仅用于诊断)。 test/mock-lifecycle.test.mjs 现在用「直接读服务就抛错」的假 ctx 复现该契约, 防止回归。


8. 已知限制

  1. 工程根目录是进程级的(已缓解)。 DSH 官方 MCP 桥接器的 cwd 只接受静态字符串 (z.string(),传函数会校验失败),而 MCP 服务器进程长驻,所以 process.cwd() 在启动时就固定了。影响面仅限两个以 cwd 为基准的调用: android_discover_project(无参数)与 android_create_app 的相对 dir; 其余工具都接受显式 projectDir / 路径参数。

    三条改法,按推荐顺序:

    • android_set_project_dir(模型自己调) —— 系统提示词已写明「找不到 Gradle 根就调它」, 所以是自愈的;不带参数即对齐当前会话工作目录。

    • /android dir <path> —— 给人用的等价命令。

    • projectDir 设置 —— 想固定成一个路径时用。

    三者都是「销毁并按新根目录重建桥接器」(约 1 秒,23 个工具重新注册), 不是真正的 per-session 路由:同一时刻只有一个工程根目录。 彻底解决:用 @deepseek-ai/dsh-scope 的 per-agent scope 为每个会话懒启动一个 cwd 正确的子进程、按会话路由调用。代价是 23 个工具要自己注册与转发, 放弃官方桥接器 —— 在有真实多工作区并发需求之前不值得。

  2. 重连预算耗尽后工具会消失。 这是官方桥接器的既定行为(连续 10 次失败后注销工具, 只能靠重载插件恢复)。排查顺序:/android 看 Node 运行时与路径 → android_preflight 看环境。

  3. Linux 不受支持(上游 P0 决策),android_preflight 会报告为不支持的宿主。

  4. 上游 TypeScript 源码未公开。 插件在 ZCode 仓库里以 apps/zcode-cli/packages/android-emulator-plugin 出现在 pnpm-lock.yaml, 但不在开源快照中,zai-org 名下也没有对应仓库。因此本插件 vendor 的是 ZCode 随包分发的编译产物 —— 它是唯一忠实的来源。dist/providers/*.js 是未混淆的 esbuild 输出,随包保留以便审计。

  5. ABI 拆分工程:android_build_app 不按 ABI 选 APK。 上游 pickApk() 只按 variant 目录匹配,取第一个命中项。工程若开了 splits.abi(例如同时出 arm64-v8a 与 x86_64),android_build_app / android_build_and_run 会返回arm64 那个, 在 x86_64 模拟器上属于错配。

    实测结论(2026-09-25,Windows + dj AVD = android-36 google_apis x86_64): android_build_and_run 确实选了 app-arm64-v8a-debug.apk,但 adb install 仍返回 Success —— 因为该 x86_64 镜像带 ARM 二进制转译,arm64 包能装也能跑, 只是走转译层。所以这不是一个必然失败,而是潜在脆弱点:换成不带转译的镜像、 或真机 arm64 配只出 x86_64 的包时,就会以 INSTALL_FAILED_NO_MATCHING_ABIS 失败。

    规避:显式调用 android_install_app 并传正确的 apkPath(用 android_discover_project 的 apks 列表挑选),不要依赖 build_and_run 的自动选择。 android-dev 技能里也写了这一条。

  6. android_create_avd 不传 device 会建出废 AVD。 上游把 device 做成可选参数, 不传时 avdmanager 落到最小默认硬件档案 —— 实测 hw.ramSize = 96M、 hw.lcd 320×640@160dpi。这种 AVD 能启动、能出现在 adb 里,几秒后自己死掉, 症状很像「工具坏了」。必须显式传 device(如 medium_phone); 建完核对 config.ini 里 hw.ramSize >= 1024M。 实测对比:不传 → 96M/320×640;传 medium_phone → 1536M/1080×2400/4 核,正常。

  7. 死掉的模拟器进程仍占着 AVD 锁。 若 android_start_emulator 返回了 serial 但设备随即消失,通常是上一个 emulator.exe / qemu-system-* 还挂着。上游的 runningAvd() 守卫是问 adb 的,而 adb 已经看不到这具尸体,于是它又启了第二个实例, 两个互踩。处理:杀掉残留的 emulator.exe / qemu-system-*,adb kill-server 重启 adb,再重试 —— 不要反复调 android_start_emulator。


9. 来源与许可

本插件采用 MIT,并对上游做双重署名(代码注释 + 本文档)。

内容

来源

许可

vendor/zcode-android-emulator/**

ZCode 官方 android-emulator@zcode-plugins-official v0.1.0,© Z.ai,逐字节复制

MIT(见其 package.json / .zcode-plugin/plugin.json)

assets/android-dev/INSTALL_ENVIRONMENT.md

同上,逐字节复制

MIT

assets/android-dev/SKILL.md

上游技能文档的衍生作品:保留全部工作流,仅改写宿主集成章节

MIT

src/、build.mjs、test/、tools/

本移植新增

MIT

上游 dist/mcp/server.js 内嵌 @modelcontextprotocol/server 与 zod;上游以 legalComments: "none" 构建,bundle 内不留第三方声明,故在此说明。 ZCode 仓库本体为 Apache-2.0(LICENSE / NOTICE.md / THIRD-PARTY-NOTICES.md)。

vendor/ 里的原始副本永不被手工编辑 —— test/vendor-integrity.test.mjs 会校验哈希, npm run vendor:check 可与上游目录直接比对。


10. 开发

node tools/vendor-upstream.mjs --from "<ZCode android-emulator-plugin 目录>"   # 重新 vendor
node tools/vendor-upstream.mjs --check                                        # 与上游比对
node tools/link-dev-deps.mjs                                                  # 从 DSH profile 链接 @deepseek-ai/*(测试用)
npm run build                                                                 # esbuild → lib/index.js
npm test                                                                      # 仅测试
npm run verify                                                                # 构建 + 测试

node_modules/、lib/ 均不入库;插件没有运行时依赖(@deepseek-ai/* 全部是 optional peer,由 DSH 宿主提供)。

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    Enables comprehensive control of Android devices via ADB for Flutter development, UI testing, and visual QA workflows. Provides 60+ tools for device management, UI inspection, app testing, performance profiling, and debugging through natural language.
    77
    MIT
  • A
    license
    C
    quality
    D
    maintenance
    Automates Android Virtual Device operations by enabling users to start emulators, execute development commands, and capture screenshots. It facilitates Android testing workflows by returning command outputs and visual feedback directly to AI assistants.
    1
    4 npm
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables AI assistants to interact with Android devices and emulators via ADB, providing tools for screenshots, UI inspection, touch and text input, app management, and device control.
    42
    82 npm
    19
    MIT