Developer Device Platform (DDP) 实战指南:远程 Android 设备租用、ADB 隧道与会话全生命周期管理
【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills
本文基于当前仓库中 developer-device-platform-basics 技能 编写,系统讲解 Developer Device Platform(DDP)的认证配置、设备查询、会话租用、ADB 端口转发与会话过期时间管理等完整链路。读完后,你可以独立完成:通过gcloud与 REST API 租用一台远程 Android 设备、建立本地 ADB 隧道连接该设备,并对会话状态、过期时间与资源释放进行全生命周期管理。
DDP 是什么,以及本技能的定位
Developer Device Platform(DDP)是 Google 全托管的全球性基础设施,提供对多种物理和虚拟设备的访问能力。需要特别注意两点边界:
- DDP 目前处于Preview(预览)阶段,行为与接口可能随版本演进;
- 本技能仅覆盖Android 远程设备场景:租用远程设备、建立连接隧道、查询会话状态、延长/取消租约。不适用于 iOS 设备或本地设备/硬件问题。
技能元数据(见 SKILL.md 头部 frontmatter)将其归类为CloudInfrastructureAndServices,并明确要求:所有 devicerun 与 devicestreaming API 操作(租用、状态查询、停止/取消、更新、列出会话)都必须使用各 reference 文档中给出的精确 curl 命令。仓库内每个操作都有对应的参考文件:
| 操作 | 参考文件 |
|---|---|
| 查询设备详情 | describe_device.md |
| 租用设备 | reserve_device.md |
| 查询会话状态 | session_status.md |
| 启动 ADB 转发器 | start_adb_forwarder.md |
| 取消会话 | cancel_session.md |
| 更新会话过期时间 | update_session_expiration.md |
| 列出活跃会话 | list_sessions.md |
| 查看设备屏幕 | view_device.md |
环境准备与认证配置
在发送任何 API 请求前,必须先正确初始化环境。SKILL.md 将其列为 CRITICAL 步骤,完整流程如下。
首先确认gcloud可执行文件是否存在;若缺失,需先按官方 Google Cloud CLI 安装指南安装。然后执行:
1. 配置 Google Cloud 认证与应用默认凭证(ADC):
gcloud auth login --no-browser gcloud auth application-default login --no-browser2. 启用所需 API(若尚未启用):
gcloud services enable devicerun.googleapis.com devicestreaming.googleapis.com testing.googleapis.com --quiet注意:Preview 阶段下,Device Streaming API 依赖 Cloud Testing API,因此必须一并启用testing.googleapis.com。
3. 安装 gcloud beta 组件(device-run命令位于 beta 命名空间):
gcloud components install beta4. 设置环境变量(后续所有 curl 命令都依赖这两个变量):
export PROJECT_ID=$(gcloud config get project) export ACCESS_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)5. Python 虚拟环境:ADB 转发器脚本的 Python 环境准备见下文启动 ADB 转发器 一节。
查询可用设备与设备详情
开始会话前,需要确定两个关键参数:modelCode(设备型号)与osVersion(系统版本)。
1. 列出可用机型:
gcloud beta device-run devices list输出中的ID列即为CATALOG_ID参数取值(例如shiba-36),后续用它来描述特定设备。
2. 查询设备详情(获取supportedProducts、分辨率、可用性等):
curl \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ "https://devicerun.googleapis.com/v1alpha/projects/${PROJECT_ID}/locations/global/devices/${CATALOG_ID}"该请求来自 describe_device.md,要点是:host 为devicerun.googleapis.com,版本路径为v1alpha,设备资源位于projects/{PROJECT_ID}/locations/global/devices/{CATALOG_ID}。
租用设备会话:完整流程
当需要租用(reserve)或连接一台设备时,SKILL.md 定义了一个严格的 8 步流程,其中包含两条强制性的业务规则。
检查设备可用性
先用上一节的查询方式获得CATALOG_ID,再按 describe_device.md 的 curl 命令检查其可用性。关键判定条件:
- 设备的
supportedProducts中必须包含deviceStreaming才可被租用; - 若用户未指定具体设备,使用默认值
CATALOG_ID=shiba-34(即 SDK 34 上的 Pixel 8); - 若用户未指定
OS_VERSION,应让用户从设备列表中选择版本,优先选择可用性最高的版本("available": "AVAILABILITY_HIGH"); - 若某个
OS_VERSION不支持deviceStreaming,不要租用,应提示用户改用其他版本。
提取参数并租用设备
从设备详情中提取:
model_id:设备详情中的modelCode(必填);version_id:设备详情中的osVersion(必填)。
规则(必须遵守):显式用户确认。租用设备会产生计费并创建云资源。操作前必须向用户明确警告当前 Google Cloud 项目(如${PROJECT_ID})将产生的费用,并停下来等待用户明确批准后才能执行租用命令。
批准通过后,发送租用请求(命令来自 reserve_device.md):
curl -s -X POST \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "androidDevice": { "androidModelId": "{model_id}", "androidVersionId": "{version_id}" } }' \ "https://devicestreaming.googleapis.com/v1/projects/${PROJECT_ID}/deviceSessions"注意此处 host 与查询设备时不同:租用走的是devicestreaming.googleapis.com且路径为v1(无locations/global段)。从响应中解析出session_name(格式如projects/${PROJECT_ID}/deviceSessions/session-xxxxxx);若租用失败则向用户报告错误。
轮询等待会话变为 ACTIVE
会话创建后处于异步供给(provisioning)状态,需要轮询其状态直至"state"为"ACTIVE"(curl 命令见 session_status.md):
curl -s -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://devicestreaming.googleapis.com/v1/{session_name}"轮询纪律有明确数值约束:
- 每 5 秒轮询一次,避免触发 API 限流;
- 若 2 分钟内未变为 ACTIVE(正常通常 1 分钟内完成),报告失败并取消该会话;
- 变为 ACTIVE 后,从会话 JSON 中提取
expireTime,并转换为用户本地时区的可读格式(例如 "June 9, 2026 at 2:44 PM PDT")展示。
启动 ADB 转发器:把本地 adb 接到远程设备
会话 ACTIVE 之后,需要启动 ADB 转发器脚本,把本地 ADB 流量转发到远程设备。该脚本随仓库提供:scripts/demo_adb_forwarder.py,依赖清单为 scripts/requirements.txt。
Python 环境准备
按 start_adb_forwarder.md 的说明,当前工作目录必须是脚本所在目录(因为后续命令使用相对路径),然后:
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt每次执行转发器脚本前都必须先激活该虚拟环境。requirements.txt 声明的三个依赖及其版本下限为:google-cloud-devicestreaming>=0.5.0、google-auth>=2.50.0、absl-py>=2.0.0。
运行转发器并解析端口
先计算会话剩余寿命(expireTime与当前时间之差,单位秒),使转发器的存活期与会话对齐,然后在本地虚拟环境中以后台方式运行:
python3 demo_adb_forwarder.py --device_session {session_name} --ttl {remaining_seconds}务必记录返回的Command ID(后台进程 ID),后续停止会话时需要用它终止进程。
脚本的参数定义(见 demo_adb_forwarder.py):
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
--device_session | 是 | 无 | 会话完整资源名,格式必须为projects/<project>/deviceSessions/<session-id> |
--access_token | 否 | 无(回退 ADC) | OAuth2 access token |
--ttl | 否 | 900 秒 | 转发器存活时长(秒),必须为正整数 |
然后监控后台输出,等待日志行ADB forwarding listening on localhost:{port}出现并提取{port};若 2 分钟内未出现(正常通常 1 分钟内),应终止转发器后台进程、取消会话并报告失败。
源码剖析:转发器如何工作
从源码看(demo_adb_forwarder.py),该脚本本质上是一个本地 TCP 服务与 gRPC 双向流之间的 ADB 协议桥,理解它能帮助你在排障时定位问题:
ADB 协议编解码。脚本头部定义了 ADB 协议四种命令魔数:
CNXN、OPEN、OKAY、CLSE、WRTE(L24-L29)。每个 ADB 包是 24 字节小端头(<6I:cmd, arg0, arg1, 载荷长度, 保留位, magic)加可选载荷,其中magic = cmd ^ 0xFFFFFFFF(send_packet),客户端回传方向则按struct.unpack("<6I", header)反向解析(L182-L184)。本地监听端口。
AdbForwarder.start()通过asyncio.start_server(..., "127.0.0.1", 0)在回环地址上绑定随机空闲端口(0表示由系统分配),随后从 socket 取出实际端口号(L100-L105)——这就是日志中localhost:{port}端口的来源。gRPC 双向流。转发器调用
DirectAccessServiceAsyncClient.adb_connect()建立到devicestreaming.googleapis.com的双向流,并携带两个 gRPC metadata:x-omnilab-session-name(完整会话资源名)与x-goog-user-project(项目 ID)(L121-L139)。首条消息以stream_id=1000打开shell:echo 'Establishing a connection'服务,用于先行探测链路。请求/响应双向桥接。本地 ADB 客户端发来的
OPEN请求被转换为AdbMessage.open_投入requests_queue(容量 128)经 gRPC 上送;设备侧返回的stream_status/stream_data再按对应stream_id映射回本地 writer,转成OKAY/WRTE/CLSE包回传(client_connected 与 receive_grpc_messages)。值得注意的是,reverse:开头的反向端口转发请求会被显式拒绝并回发 FAIL 包(reject_reverse)——即转发链路只支持客户端到设备方向,不支持设备反向端口映射。TTL 自毁与清理。
main()中 gRPC 超时设为TTL + 10秒缓冲(_TIMEOUT_BUFFER_SECONDS = 10),asyncio.wait_for在 TTL 到期后触发stop()(L340-L363)。stop()会依次执行adb disconnect localhost:{port}、关闭监听 server、关闭 gRPC 传输(L292-L315),保证到期后不残留连接。自动接入。转发器启动成功后还会自动执行
adb connect localhost:{port}(L149-L157),使设备直接进入本地adb devices列表。
会话就绪后的交付信息
端口就绪后,先运行以下命令获取设备型号:
adb -s localhost:{port} shell getprop ro.product.model然后直接向用户输出一条就绪信息(SKILL.md 明确要求在聊天中打印,不要创建任何 artifact 文件),格式为:
Device Model: {device_model} OS Version: {version_id} ADB Address: localhost:{port} Session Expiration: {expire_time_human_readable_local}最后一步同样关键:把{session_name}与{command_id}保存到会话上下文中,供后续清理使用。
查看远程设备屏幕(可选)
编码代理可以直接用adb操作远程设备;若用户希望可视化查看屏幕并手动操控,view_device.md 推荐开源工具 scrcpy(安装前必须先征得用户同意),用法示例:
scrcpy -s localhost:{port} --force-adb-forward修改会话过期时间
当用户要求延长活跃会话时,同样适用"显式用户确认"规则:延长会话会产生额外费用,必须先警告${PROJECT_ID}项目将新增计费并等待批准。随后:
1. 提取参数:
session_name:活跃会话名;ttl:新的剩余时长(如3600s);若用户以其他形式给出时长,需换算为该格式。
2. 通过 API 修改会话(命令来自 update_session_expiration.md):
curl -s -X PATCH \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "ttl": "3600s" }' \ "https://devicestreaming.googleapis.com/v1/{session_name}?updateMask=ttl"3. 重启连接转发器。由于旧转发器携带的是旧 TTL,必须:
- 运行
adb disconnect localhost:{port}确保旧转发连接已关闭; - 停止对应
{command_id}的旧转发器进程; - 按"租用设备会话"第 5 步重新启动转发器——重新计算新的
--ttl秒数并记录新的 Command ID。
4. 向用户确认会话时长已更新、转发器已按新 TTL 重启。
停止会话与辅助查询
停止 / 释放设备
当用户要求停止、清理或释放设备时:
定位会话:从上下文中取出
{session_name}与{command_id};若已丢失,先列出活跃会话(见下文辅助命令)找到会话名;通过 API 取消会话(命令来自 cancel_session.md,空 body 的 POST 到
:cancel端点):curl -s -X POST \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ -d "{}" \ "https://devicestreaming.googleapis.com/v1/{session_name}:cancel"终止连接转发器:用环境提供的进程管理能力终止匹配
{command_id}的后台进程;确认:告知用户会话已取消、资源已释放。
辅助:列出活跃会话
上下文丢失时,可用 list_sessions.md 中的命令列出项目内会话并过滤ACTIVE状态:
curl -s -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://devicestreaming.googleapis.com/v1/projects/${PROJECT_ID}/deviceSessions" \ | python3 -c " import sys, json data = json.load(sys.stdin) sessions = data.get('deviceSessions', []) active = [s for s in sessions if s.get('state') == 'ACTIVE'] print(json.dumps(active, indent=2)) "关键要点速查
| 要点 | 说明 | 依据 |
|---|---|---|
| Preview 阶段 | DDP 处于预览状态,接口路径含v1alpha(devicerun)与v1(devicestreaming)之分 | SKILL.md |
| 必需 API | devicerun+devicestreaming+testing(Preview 期依赖)三个服务 | SKILL.md |
| 可租用条件 | supportedProducts含deviceStreaming | SKILL.md |
| 默认设备 | CATALOG_ID=shiba-34(Pixel 8, SDK 34) | SKILL.md |
| 计费确认 | 租用/延长前必须显式警告费用并等待用户批准 | SKILL.md |
| 轮询节奏 | 状态轮询间隔 5 秒;2 分钟超时(会话供给与转发器上线均适用) | SKILL.md、start_adb_forwarder.md |
| 转发器 | 本地127.0.0.1随机端口 <-> gRPCadb_connect双向流;TTL 默认 900 秒,到点自动adb disconnect清理 | demo_adb_forwarder.py |
| 上下文保存 | 必须记录session_name与command_id以便后续清理 | SKILL.md |
按以上流程操作,即可以可复现的方式完成一次完整的 DDP 远程 Android 设备使用闭环:认证配置 → 选机 → 租用 → 隧道连接 → 使用 → 续期/释放。
【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考