文章目录
- [不用 root:我用 Tailscale 打通了云手机的 ADB 通道](#不用 root:我用 Tailscale 打通了云手机的 ADB 通道)
-
- TL;DR
- 一、起因:一台"不听话"的云手机
- 二、先想清楚:问题到底出在哪
- [三、第一次尝试:IPv6 直连(失败)](#三、第一次尝试:IPv6 直连(失败))
- 四、关键转折:需要一个"牵线的人",但不一定是一台服务器
-
- [4.1 Tailscale 到底做了什么(大白话版)](#4.1 Tailscale 到底做了什么(大白话版))
- [4.2 为什么这条路能绕开"没有公网 IP"](#4.2 为什么这条路能绕开"没有公网 IP")
- [五、另一半拼图:无线调试让 adb 不用 root 也能开门](#五、另一半拼图:无线调试让 adb 不用 root 也能开门)
- [六、实操:从零到 `adb devices`](#六、实操:从零到
adb devices) -
- [6.1 云手机侧](#6.1 云手机侧)
- [6.2 电脑侧](#6.2 电脑侧)
- [6.3 配对与连接](#6.3 配对与连接)
- [6.4 固化端口为 5555](#6.4 固化端口为 5555)
- 七、踩坑记录
-
- [坑 1:Termux 里 `su` 找不到](#坑 1:Termux 里
su找不到) - [坑 2:Termux 的 curl 符号缺失](#坑 2:Termux 的 curl 符号缺失)
- [坑 3:无线调试端口会漂移](#坑 3:无线调试端口会漂移)
- [坑 4:cmd 和 PowerShell 语法不一样](#坑 4:cmd 和 PowerShell 语法不一样)
- [坑 5:切换端口后出现 `offline` 僵尸设备](#坑 5:切换端口后出现
offline僵尸设备) - [坑 6:配对端口 ≠ 连接端口](#坑 6:配对端口 ≠ 连接端口)
- [坑 1:Termux 里 `su` 找不到](#坑 1:Termux 里
- [八、意外发现:这台云机的 adbd 居然是 root](#八、意外发现:这台云机的 adbd 居然是 root)
- [九、能力验证:adb 到底能不能用](#九、能力验证:adb 到底能不能用)
-
- [adb 能力边界](#adb 能力边界)
- 十、这套方案适合谁
-
- 适合
- [不适合 / 需要换路](#不适合 / 需要换路)
- 还有一个必须提前确认的点
- 十一、日常维护:重启之后怎么办
- 十二、结语
- [附录 A:命令速查](#附录 A:命令速查)
- [附录 B:检查清单](#附录 B:检查清单)
- [附录 C:本次实测环境](#附录 C:本次实测环境)
不用 root:我用 Tailscale 打通了云手机的 ADB 通道
摘要:云手机没有公网 IP、没有 root、厂商也不提供 ADB 入口,常规教程都要求你租一台云服务器跑 frp。本文记录另一种做法:用 Tailscale 组一个虚拟局域网,配合 Android 11 的「无线调试」,在两个约束(不租服务器、不 root)下拿到完整的 adb 控制能力。包含原理、实操、踩坑和实测的能力边界。
关键词:云手机、ADB、Tailscale、无线调试、Android 自动化、NAT 穿透
TL;DR
- 云手机和本地电脑都装 Tailscale,登录同一账号
- 云手机打开 开发者选项 → 无线调试 (Android 11+,不需要 root)
- 电脑执行
adb connect 100.x.y.z:<端口> - 可选:
adb tcpip 5555把随机端口固化成 5555 - 结果:零服务器、零 frp、零 Termux、零 root,adb 全功能可用
一、起因:一台"不听话"的云手机
我手上有一台桃心云手机(型号 Mate 30pro),想从本地电脑用 adb 控制它,做 UI 自动化。
正常手机插根 USB 就行,云手机不行:它在云厂商的 VPC 里,地址是 172.18.15.x,外面根本路由不到。厂商也没有提供任何 ADB 入口。
搜到的教程几乎都是同一个模板:
云手机装 Termux → 装 proot → 修 DNS → 装 frpc
云服务器装 frps → 开放端口 → 配 token
电脑装 frpc visitor → adb connect 127.0.0.1:55556
这套方案本身没问题,我在另一台云机上跑通过。但它有两个硬性前提:
- 你必须有一台公网服务器(对应本文的约束一:不想租)
- 云手机必须有 root(对应约束二:这台没有)
更麻烦的是,这类教程里的坑极多:Termux 的 curl 版本错配、Go 程序的 DNS 解析失败、Android 13 的 adbd 只听 IPv6、Termux 粘贴长 URL 被输入法污染......我上一台机器全部踩过一遍。
所以这次我想找一个更简单的路。
二、先想清楚:问题到底出在哪
很多人把这件事理解成"需要一条隧道"。但隧道只是手段,真正的问题是:
两台机器互相找不到对方。
看一下双方的地址:
| 设备 | 地址 | 别人能访问吗 |
|---|---|---|
| 云手机 | 172.18.15.19 |
❌ 厂商 VPC 内部地址 |
| 我的电脑 | 192.168.1.100 |
❌ 家庭局域网地址 |
| 我的电脑 | 240e:878:...:17a2 |
✅ 公网 IPv6 |
云手机没有公网地址;电脑有一个公网 IPv6。
于是有一个看起来很美妙的思路:
让云手机主动连到我的电脑,走 IPv6,不就完全不需要服务器了吗?
这个思路在逻辑上完全成立------只要云手机也有 IPv6,并且能路由到我家。
三、第一次尝试:IPv6 直连(失败)
先在云手机(Termux)里测一下能不能用 IPv6 出网:
bash
curl -6 -sS -o /dev/null -w "HTTP=%{http_code} IP=%{remote_ip}\n" --max-time 10 https://www.baidu.com
结果:
curl: (7) Failed to connect to www.baidu.com:443 after 149 ms: Could not connect to server
加上 -v 看细节:
* IPv6: 240e:ff:e020:98c:0:ff:b061:c306, ...
* IPv4: (none)
* Trying [240e:ff:e020:98c:0:ff:b061:c306]:443...
* Immediate connect fail for 240e:ff:e020:98c:...: Network is unreachable
* connect to 240e:ff:e020:98c:... port 443 from :: port 0 failed: Network is unreachable
注意这一句:
from :: port 0
from :: 的意思是内核找不到任何可用的源 IPv6 地址 ------不是被防火墙拦了,也不是 DNS 问题,而是这台云手机压根没有被分配 IPv6 地址。
结论很干脆:
云手机:只有私网 IPv4,无 IPv6
电脑: 私网 IPv4 + 公网 IPv6
↑
双方都没有对方能到达的地址
直连这条路,在物理层面就断了。
顺带说一句:如果当时云手机真有公网 IPv6,这条路是可行的------电脑起一个 frps,云机主动反向连过去即可,同样不需要额外的服务器。所以这个检查值得每次换云机时都做一遍。
四、关键转折:需要一个"牵线的人",但不一定是一台服务器
直连不行,那就必须有第三方帮忙牵线。传统做法是自己租一台公网服务器跑 frp,但牵线这件事本身,已经有成熟服务免费做了。
这就是 Tailscale 这类 P2P 组网工具的价值。
4.1 Tailscale 到底做了什么(大白话版)
把 Tailscale 想成一个私人快递网络:
- 登记 :两台设备都主动向 Tailscale 的协调服务器发起出站连接(出站不受 NAT 限制)
- 发牌 :协调服务器给每台设备分配一个虚拟地址(
100.x.y.z) - 牵线 :把双方信息互换,两端尝试直接打洞
- 打通 → 数据 P2P 直连,速度快
- 打不通 → 走 DERP 中继,能通但慢一点
- 加密:全程 WireGuard 加密,即使走中继,中继也看不到内容
关键在于:协调服务器只负责牵线,不承载业务流量(除非走中继),所以你不需要"租一台服务器并维护它"。
分配完地址后,100.70.52.94 就是云手机在虚拟局域网里的门牌号,你的电脑访问它,就像访问同一局域网里的设备。
你的电脑 100.95.223.67 ══► 100.70.52.94 云手机
(Tailscale 虚拟网络,WireGuard 加密)
4.2 为什么这条路能绕开"没有公网 IP"
因为两端都是主动往外连:
- 云手机 → Tailscale 协调服务器:出站,NAT 允许
- 电脑 → Tailscale 协调服务器:出站,NAT 允许
只要云手机能上网(能 apt 装包就说明能),就能被拉进这个网络。这和云手机有没有公网 IP、有没有 IPv6 完全无关。
五、另一半拼图:无线调试让 adb 不用 root 也能开门
网络通了,但云手机里还得有个"接活的管家"------adbd(adb 守护进程)。
平时 adbd 只听 USB。要让它监听网络端口,传统做法是:
bash
setprop service.adb.tcp.port 5555
stop adbd
start adbd
这三条命令需要 root。 这就是为什么旧教程都强调"云机必须 root"。
但 Android 11(API 30)之后,系统设置里直接提供了**「无线调试」**开关:
设置 → 开发者选项 → 无线调试 → 打开
打开后,系统自己就把 adbd 启动在 TCP 模式上,监听一个随机高位端口。不需要 root,不需要 setprop,不需要重启 adbd。
打开后云机会显示:
IP 地址和端口
172.18.15.19:37085
这个 37085 就是 adbd 的门牌号。
于是整个方案的两个前提就都满足了:
| 问题 | 解法 |
|---|---|
| 两台机器互相找不到 | Tailscale 虚拟局域网 |
| adb 开不了门 | Android 11+ 无线调试 |
服务器和 root,一个都不需要。
六、实操:从零到 adb devices
下面是浓缩版流程。每一步都带验证方法。
6.1 云手机侧
① 打开开发者选项
设置 → 关于手机 → 连点「版本号」7 次。
② 打开无线调试
设置 → 系统 → 开发者选项 → 无线调试 → 打开。
记下屏幕上的端口(后面会变,先记着)。
③ 安装 Tailscale
- 有 Google Play:搜索 Tailscale,开发者认准 Tailscale Inc.
- 没有:用云机浏览器打开
https://pkgs.tailscale.com/stable/tailscale-latest.apk直接装
④ 登录并连接
打开 Tailscale → Sign in → 用 Google / Microsoft / GitHub 登录 → 点开关 → 系统弹出 VPN 授权 → 允许。
连接成功后会显示:
mate-30pro.tailXXXX.ts.net
100.70.52.94 ← 记住这个
fd7a:115c:a1e0::xxxx:xxxx
⑤ 关掉电池优化
设置 → 应用 → Tailscale → 电池 → 无限制。
不做这一步,Android 的 Doze 会在后台杀掉 VPN,表现为"用着用着突然断了"。
⑥ 关闭密钥过期(强烈建议)
浏览器打开 https://login.tailscale.com/admin/machines → 找到云机 → ... → Disable key expiry。
云机重新登录很麻烦,关掉省事。
6.2 电脑侧
① 安装 Tailscale
powershell
winget install --id Tailscale.Tailscale -e
或用官网安装包。装完登录同一个账号,点 Connect。
验证:
powershell
tailscale status
预期能看到云机:
100.95.223.67 my-pc xxx@ windows -
100.70.52.94 mate-30pro xxx@ android -
② 确认 adb 可用
cmd
adb version
需要 platform-tools 30.0.0 以上(adb pair 随 Android 11 一同提供)。建议直接用最新版。
6.3 配对与连接
新设备第一次连接需要配对(我的这台之前配对过,所以直接连上了,但新云机大概率要配对)。
云机:无线调试 → 使用配对码配对设备
屏幕会显示:
Wi-Fi 配对码
123456
IP 地址和端口
172.18.15.19:41234 ← 注意这个是配对端口
电脑:
cmd
adb pair 100.70.52.94:41234
⚠️ 这里有一个非常容易踩的坑 :云机屏幕上显示的 IP 是它的内部地址 (
172.18.15.19),你根本连不上。要把 IP 换成 Tailscale 地址 ,端口保持屏幕上的配对端口不变。
然后输入 6 位配对码:
Enter pairing code: 123456
Successfully paired to 100.70.52.94:41234
接着连接连接端口(不是配对端口):
cmd
adb connect 100.70.52.94:37085
验证:
cmd
adb devices -l
100.70.52.94:37085 device product:CHUNMEI model:Mate_30pro ...
device 状态就代表完全可用。
再确认 shell:
cmd
adb shell echo ok
6.4 固化端口为 5555
无线调试的端口每次重启都会变(我这次就从 37085 漂移到了 33189)。固化一次省心:
cmd
adb tcpip 5555
输出:
restarting in TCP mode port: 5555
注意:这一瞬间 adbd 会重启,当前连接会断开,属正常现象。重新连:
cmd
adb connect 100.70.52.94:5555
之后只要云机没重启,永远用 5555。
边界 :
adb tcpip在云机重启后失效,会回到随机端口模式,需要重新做一遍。
七、踩坑记录
坑 1:Termux 里 su 找不到
~ $ su -c id
No su program found on this device. Termux does not supply tools for rooting...
command -v su 返回的是 Termux 自己的包装脚本 /data/data/com.termux/files/usr/bin/su,不是真的 su。
但这不影响本方案------因为根本不需要 root,也不需要 Termux。这是我一开始按旧方案排查时走的弯路。
有意思的是,后来发现
adb shell id显示uid=0(root)。原因是 adbd 本身以 root 运行,和 Termux 是两回事:Termux 是普通 APP(uid 10xxx),看不到系统的 su;adbd 是系统服务,本来就有 root。二者上下文不同。
坑 2:Termux 的 curl 符号缺失
CANNOT LINK EXECUTABLE "curl": cannot locate symbol
"nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation"
referenced by "/data/data/com.termux/files/usr/lib/libcurl.so"
补了 libnghttp2 之后又冒出:
cannot locate symbol "libssh2_crypto_engine"
原因是这台云机的 Termux 环境里有一批库停留在老版本,而 libcurl 升级到了 8.23,依赖链对不上。逐个补会一直冒新错,正确做法是一次性升级:
bash
pkg upgrade -y
(中途问 openssl.cnf (Y/I/N/O/D/Z) 直接回车选默认)
升级后:
curl 8.23.0 ... OpenSSL/3.6.5 libssh2/1.11.1 nghttp2/1.70.0
这个坑只在"用 Termux 验证网络"时遇到,最终方案不用 Termux,所以可以完全跳过。
坑 3:无线调试端口会漂移
同一台云机,先显示 37085,后来变成 33189。每次关开无线调试、云机重启都会变。
所以要么每次回云机屏幕看一眼,要么用 adb tcpip 5555 固化。
坑 4:cmd 和 PowerShell 语法不一样
在 cmd 里执行 PowerShell 写法:
cmd
adb exec-out screencap -p > "$env:USERPROFILE\Desktop\a.png"
报:
文件名、目录名或卷标语法不正确。
因为 cmd 不认识 $env:,而且 : 在路径里非法。
通用写法(cmd / PowerShell 都能跑):
cmd
adb shell screencap -p /sdcard/screen.png
adb pull /sdcard/screen.png D:\adb_test\screen.png
顺便还验证了文件传输能力。
坑 5:切换端口后出现 offline 僵尸设备
adb tcpip 5555 之后,adb devices 里会同时出现:
100.70.52.94:37085 offline
100.70.52.94:5555 device
旧的 37085 记录已经是僵尸,清理即可:
cmd
adb disconnect 100.70.52.94:37085
如果报 error: no such device,说明已经自动清掉了,忽略。
坑 6:配对端口 ≠ 连接端口
云机无线调试页面上:
- 「IP 地址和端口」 → 用于
adb connect - 「使用配对码配对设备」里的端口 → 用于
adb pair
这是两个不同的端口。用错会直接连不上。
八、意外发现:这台云机的 adbd 居然是 root
连接建立后,我随手跑了一下:
cmd
adb root
输出:
adbd is already running as root
再查身份:
cmd
adb shell id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),...
context=u:r:shell:s0
uid=0 但 context=u:r:shell:s0 ------ 这是整份记录里最关键的一行。
它说明:
- DAC 层面:进程是 root,文件权限位对它不设限
- MAC 层面 :SELinux 域仍是
shell,不是su/magisk
继续查构建信息,结果更有意思:
| 属性 | 值 | 标准 AOSP 的含义 |
|---|---|---|
ro.build.type |
user |
adbd 应以 shell 运行 |
ro.debuggable |
0 |
不可调试 |
ro.secure |
1 |
adbd 应降权 |
getenforce |
Enforcing |
SELinux 生效 |
按标准 AOSP,这三个属性都意味着 adb root 应该失败。但这台成功了 ,而且 ls /data/data 能列出 118 个应用目录。
结论:这是厂商魔改过的镜像 ------在 user 构建上打了补丁,让 adbd 无条件以 root 运行,同时放宽了 shell 域对 /data/data 的读取。
能力定位
普通 shell < 这台云机(uid=0 + SELinux shell 域) < Magisk 完全体
- ✅ 能读更多文件、能
pm grant、能看应用数据目录 - ⚠️ 写系统分区、改
persist.*大概率仍被 SELinux 拦 - ❌ 不是真正的"完全 root"
这个差异因厂商而异。 换一台云机,上面这些结论可能完全不同,建议每台都实测记录。
九、能力验证:adb 到底能不能用
光看到 device 不够,我做了一层能力验证:
| 验证项 | 命令 | 结果 |
|---|---|---|
| shell 执行 | adb shell echo ok |
✅ ok |
| 截屏 | screencap + pull |
✅ 720×1300 真实画面 |
| UI 控件树 | uiautomator dump |
✅ 43 个节点,text 完整 |
| 安装 APK | adb install -r |
✅ Success |
| 启动应用 | monkey -p <pkg> -c LAUNCHER 1 |
✅ Events injected: 1 |
| 文件传输 | adb push / adb pull |
✅ |
对 UI 自动化来说,这几项已经够了:看得见(截屏)、定位得到(控件树)、点得动(input)、装得上(install)。
adb 能力边界
完整可用 :设备管理、应用安装/卸载、文件推拉、截屏录屏、UI 自动化、dumpsys、logcat、am、pm、input......
部分受限:
| 能力 | 限制 |
|---|---|
input text 中文 |
只支持 ASCII,中文需 ADBKeyboard 或输入法广播 |
settings put 某些键 |
Android 12+ 有限制 |
screenrecord |
默认最长 180 秒 |
adb backup |
Android 12+ 基本废弃 |
无 root 不可用 (除非像这台一样厂商魔改):adb root、改系统属性、直接读写 /data/data、刷机。
十、这套方案适合谁
适合
- 云手机 Android 11+
- 允许安装第三方 APK
- 不禁止 VPN(有些厂商会锁 VPNService)
- 没有 root,或不想折腾 root
- 不想为了一次 adb 连接去租服务器
- 本地电脑不在云厂商 VPC 内
不适合 / 需要换路
| 情况 | 替代方案 |
|---|---|
| Android < 11 或无线调试被裁 | 有 root → frp / SSH 反向隧道;无 root → 厂商官方 ADB |
| 云机禁止 VPN | frp + 公网服务器;ZeroTier(同样可能被禁) |
| 云机有公网 IPv6 | 可以不用 Tailscale,电脑起 frps 反向隧道 |
| 自建 redroid / 容器 | 直接 adb connect <容器IP>:5555,什么隧道都不用 |
还有一个必须提前确认的点
云机是不是"还原型"实例------重启后应用和登录状态是否保留。
如果不保留,每次重启都要重装 Tailscale、重新登录、重新开无线调试。这会让方案的日常成本大幅上升。建议先装个无关紧要的 APP,重启一次看看还在不在。
十一、日常维护:重启之后怎么办
云机重启后,按这个顺序恢复:
① 云机:确认 Tailscale 是 Connected(必要时手动连)
② 云机:开发者选项 → 无线调试 → 确认开着 → 记下当前端口
③ 电脑:adb connect 100.70.52.94:<当前端口>
④ 电脑:adb tcpip 5555
⑤ 电脑:adb connect 100.70.52.94:5555
一键脚本(端口已固化为 5555 时):
bat
@echo off
set PHONE=100.70.52.94
adb connect %PHONE%:5555
adb devices -l
adb -s %PHONE%:5555 shell echo ok
pause
断线排查顺序:
1. tailscale status → 云机那行是否 online
2. ping 100.70.52.94 → 通不通
3. 云机本地 nc -vz 127.0.0.1 5555 → adbd 在不在
4. 端口对不对
5. 配对是否失效(failed to authenticate)
6. adb kill-server / start-server
十二、结语
这套方案的本质,是把"需要自己维护的基础设施"换成了"已经存在的服务":
| 旧方案 | 新方案 |
|---|---|
| 租云服务器 + 装 frps | Tailscale 账号 |
| root + setprop 开 adbd | 系统设置里的无线调试 |
| Termux + proot + frpc | Tailscale APP |
| token + secretKey | 账号登录 |
代价是引入了对 Tailscale 这个第三方服务的依赖,以及"云机必须允许 VPN"这个新约束。但对大多数个人使用场景来说,这笔交换非常划算------从三台机器、三个进程、一堆配置文件,变成两个 APP 和一条命令。
至于那台云机 adb shell 居然是 root 的意外收获,算是个彩蛋。它提醒我一件事:云手机厂商的镜像差异极大,任何"标准结论"都要自己实测一遍。
附录 A:命令速查
cmd
:: 连接
adb devices -l
adb connect 100.70.52.94:5555
adb disconnect 100.70.52.94:5555
adb kill-server
adb start-server
:: 配对
adb pair 100.70.52.94:<配对端口>
:: 固定端口
adb tcpip 5555
:: 设备操作
adb shell <命令>
adb push <本地> <设备>
adb pull <设备> <本地>
adb install -r <apk>
adb uninstall <包名>
:: 自动化
adb shell screencap -p /sdcard/a.png
adb shell uiautomator dump /sdcard/ui.xml
adb shell input tap <x> <y>
adb shell input swipe <x1> <y1> <x2> <y2> <ms>
adb shell input keyevent <键值>
adb shell am start -n <包名>/<Activity>
adb shell am force-stop <包名>
powershell
# Tailscale
tailscale status
tailscale ping mate-30pro
附录 B:检查清单
云机
- Android ≥ 11
- 开发者选项已开
- 无线调试已开,端口已记录
- Tailscale 已装、已登录、已 Connected
- 已记录 100.x 地址
- 电池优化已关闭
- key expiry 已关闭
电脑
- Tailscale 已装、登录同一账号
-
tailscale status能看到云机 - adb ≥ 30.0.0(支持
adb pair) -
adbkey已备份(换电脑时免配对)
连接
- 已配对
-
adb connect成功 -
adb devices显示 device -
adb tcpip 5555已执行 -
adb connect ...:5555成功
附录 C:本次实测环境
| 项 | 值 |
|---|---|
| 云机 | Mate 30pro(product: CHUNMEI, device: OBXIN) |
| 云机内部 IP | 172.18.15.19 |
| 云机 Tailscale | 100.70.52.94 |
| 云机 IPv6 | ❌ 无 |
| adb shell 身份 | uid=0(root),context=u:r🐚s0 |
| SELinux | Enforcing |
| 构建属性 | user / debuggable=0 / secure=1(厂商魔改) |
| 电脑 | Windows 11 (10.0.22000) |
| 电脑 Tailscale | 100.95.223.67 |
| adb 版本 | 1.0.41 / 34.0.4 |
| 初始端口 | 37085(后漂移为 33189) |
| 固化端口 | 5555 |
说明 :文中"配对流程"按 Android 11+ 标准流程编写。实测设备因之前已配对,
adb connect直接成功,未走配对;"把配对界面显示的 IP 换成 Tailscale IP"是基于 adbd 监听所有网卡的推断,新设备首次配对时请以实际结果为准。