adb原本是为USB数据线设计的,后来扩展到了局域网。它也能跨互联网工作,而且很多团队都依赖这一点:一个区域的CI运行器连接另一个区域的手机,开发者在家里连办公室的机架,测试套件驱动云手机。但它的默认设置假定网络是可信的。配置错了,调试桥就会变成任何发现它的人都能用的远程shell。
本指南讲清adb如何通信,四种通过互联网安全延伸adb的方式及完整命令,以及一份让它保持关闭的检查清单。
adb如何通信
涉及三个部分。adb客户端是你输入的命令。adb server是同一台机器上的后台进程,监听127.0.0.1:5037,负责跟踪设备。adbd是手机上的守护进程。server通过USB连接adbd,执行adb tcpip后通过明文TCP连接,在Android 11及以上开启无线调试时则通过TLS连接。
远程使用,就是把其中一段连接拉长到互联网上:客户端到server,或者server到adbd。拉长哪一段,决定了用哪种方法。
有两个事实决定了安全格局。adbd会要求用户为每台电脑的RSA密钥授权一次,这能挡住手机从未见过的陌生人。但经典的TCP模式什么都不加密:shell输出、logcat和你推送的每个文件都以明文传输。
绝对不要做的事
不要在路由器上把5555或5037端口转发到互联网,也不要在云防火墙里开放它们。2018年,一种名为ADB.Miner的蠕虫通过在5555端口暴露adb的安卓设备传播,主要是电视盒子,把它们变成了挖矿机。扫描器无时无刻不在寻找开放的adb端口。
也不要用-a启动adb server,它会让server监听所有网络接口。任何能连上这个端口的人都能控制主机上连接的所有设备,根本不会弹出密钥确认。
方式1:SSH隧道连到远程adb server
当手机通过USB连在一台你能SSH登录的主机上时用这种方式:办公室机架、实验室机器、CI服务器。把本地端口转发到主机的adb server,再让客户端指向它:
ssh -N -L 15037:127.0.0.1:5037 user@lab-host adb -P 15037 devices ADB_SERVER_SOCKET=tcp:127.0.0.1:15037 adb -s SERIAL logcat -d
本地端口不要用5037,以免你自己的adb server和隧道冲突。安装、推送、shell和logcat都能用,因为客户端通过它与server的连接传输一切。
有一点出乎意料:adb forward这时开的端口在实验室主机上,而不是你的笔记本上。想从自己的电脑访问转发的端口,就在SSH命令里为它再加一个-L。
SSH提供你本来就信任的密钥和加密,一条隧道就能覆盖一整层架子。代价是要运营一台主机、在上面开账号,而拥有这个账号的人能控制所有连着的手机。
方式2:SSH隧道直连adbd
当手机所在的网络只能通过跳板机访问、而你又无法通过USB接触那里的机器时,用这种方式。先通过USB或无线调试把手机切换到TCP模式一次,然后建隧道连过去:
adb tcpip 5555 ssh -N -L 15555:192.168.1.50:5555 user@jump-host adb connect 127.0.0.1:15555
本地端口要选5555到5585范围之外的,因为adb server会把这个范围内的本地端口当作模拟器。手机必须保持TCP模式,而大多数系统重启后会退出这个模式;第一次连接时手机上仍会弹出密钥确认,所以需要有人去点一次接受。
方式3:经VPN的无线调试
Android 11加入了无线调试:先用配对码配对,然后建立TLS连接。它是为局域网设计的,但只要你的笔记本和手机能访问彼此的内网地址,经过WireGuard或Tailscale这类VPN也能用。在开发者选项里打开无线调试,选择使用配对码配对,然后运行:
adb pair 100.64.0.12:37099 adb connect 100.64.0.12:41235
adb pair会要求输入六位配对码。配对端口和连接端口不同,而且每次开关无线调试都会变。mDNS发现在大多数VPN上无法穿透,所以要手动输入手机上显示的地址。手机离开所在的Wi-Fi网络时,这个设置还会自动关闭,所以不适合任何无人值守的场景。
方式4:设备云中继
设备云在adbd前面放一个中继,给每台手机分配独立地址。细节因服务商而异。下面是Clousd中继的工作方式,与ADB文档中的说明一致:
- ADB默认关闭。你按手机逐台开启,并设置最多20个IP地址或地址段的白名单。
- 访问会自动过期:默认24小时后,最长7天。空闲会话30分钟后关闭。
- 你用一次性验证码登录,验证码只显示一次。输错会关闭会话,多次失败会关闭中继。
- root、remount、reverse、reboot等命令一律拒绝,每个会话都有日志。
adb connect HOST:PORT adb -s HOST:PORT shell clousd-login CODE adb -s HOST:PORT install app-debug.apk adb -s HOST:PORT logcat -d
不用托管任何东西,也不用在自己的网络上开放任何端口。代价是少数命令不能用。依赖adb reverse的工作流,比如React Native应用访问你笔记本上的Metro server,需要另想办法,例如用一个手机能通过URL访问的开发服务器。如果你本来就没有可连的硬件,云端的远程安卓手机自带中继。
四种方式对比
| SSH连adb server | SSH连adbd | 经VPN的无线调试 | 设备云中继 | |
|---|---|---|---|---|
| 手机在哪里 | 通过USB连在你控制的主机上 | 在跳板机后面的网络里 | 在你的VPN能到达的网络里 | 在服务商的数据中心 |
| 你要维护 | 主机和SSH访问 | 跳板机和TCP模式 | VPN、连着Wi-Fi的手机 | 无需维护 |
| 访问控制 | SSH密钥 | SSH密钥加adb密钥 | VPN加配对 | 白名单、过期时间、一次性验证码 |
| 手机重启后仍可用 | 是 | 通常不行 | 不行 | 是 |
adb reverse | 可用 | 可用 | 可用 | 通常被拒绝 |
| 适合 | 办公室机架、CI主机 | 临时连一次实验室 | 个人手机 | 没有硬件的团队和CI |
延迟和带宽
adb通信频繁,但对距离很宽容。往返200 ms时,交互式adb shell仍然可用,logcat也能流畅输出。安装和推送受限于你的上传速度,所以通过慢速的家庭上行链路传一个150 MB的APK要好几分钟。
插桩测试受影响最大,因为每个UI操作都要等回复。调高超时时间,把固定的sleep换成等待某个条件,而不是把sleep加长。如果通过SSH隧道用scrcpy投屏,用scrcpy --video-bit-rate=2M --max-size=1024降低码率和尺寸。在云手机上,这件事通常由浏览器画面来完成。
在CI中测试远程安卓设备
在远程手机上测试的CI任务有四步:连接、等待设备、运行、断开。如果你的中继需要登录,再加上登录这一步。
adb connect "$DEVICE" adb -s "$DEVICE" wait-for-device adb -s "$DEVICE" install -r app-debug.apk adb -s "$DEVICE" install -r app-debug-androidTest.apk adb -s "$DEVICE" shell am instrument -w com.example.app.test/androidx.test.runner.AndroidJUnitRunner adb disconnect "$DEVICE"
设备地址和登录验证码要放在CI的密钥配置里,不要放进代码仓库。应用测试页面展示了同样的运行通过REST API是什么样子,在线测试安卓应用详细介绍了冒烟测试和logcat命令。如果你在考虑还要不要自己维护机架,见安卓真机测试平台。
安全检查清单
- 永远不要把5037或5555端口暴露到互联网。
- 把
~/.android/adbkey当作凭据。它是你在每台信任过它的手机上的身份,不要复制到共用的机器上。 - 对实验室里的手机,不时在开发者选项中“撤销USB调试授权”。
- 优先选择会自动过期、只限已知地址的访问方式。
- 用完后用
adb disconnect和adb kill-server断开连接并停止server,并关闭无线调试。 - 记录谁在什么时候连接了哪台手机。


