平时我们点一下“连接”,很多时候会觉得 ADB 已经连上了。
其实不是。
真正能执行 shell 命令之前,设备和应用之间还要先完成一整套协议握手:先建立通道,再发 CNXN;如果设备要授权,就走 AUTH;授权通过后,再打开 shell:;后面还有 WRTE、OKAY、CLSE 这些消息,负责持续传输和清理。
这篇文章就从 ADB WiFi 的源码出发,把这条链路拆开讲清楚。
先把通道和协议分开
第一件事要分清:Socket 或 USB bulk endpoint 只是通道,不是协议本身。
在 ADB WiFi 里,TCP 连接会包装成 TcpChannel,USB OTG 会包装成 UsbChannel。底层一个走局域网端口,一个走 USB 设备连接,但上层的 AdbConnection 不关心这些差异。
它只关心三件事:
1. 能不能读满指定字节 2. 能不能把一个 AdbMessage写出去3. 能不能正常关闭
这就是为什么源码里会先统一通道,再谈协议。

ADB message 不是文本,是二进制包
ADB 协议的基本单位是消息。
在项目里,AdbMessage 把消息头固定成 24 个字节,小端序排列。它不是“发一行字符串”,而是按固定格式读写二进制包。
这 24 个字节里,最关键的是这几个字段:
• command• arg0• arg1• data length• data check• magic
接收消息时,代码会先读满 24 字节头;如果 payload 不为空,再继续读 payload。
所以这里的核心不是“看起来像命令行”,而是“协议层按消息格式精确收发”。
第一包 CNXN,先把会话建立起来
真正开始握手时,AdbConnection.connect() 会先发出一个 CNXN 包。
代码大概是这样:
channel.writex(AdbProtocol.generateConnect());这个包的意义很直接:告诉对方,我是 host 端,我支持这个协议版本,单次 payload 最大按这个值来。
写出 CNXN 以后,连接线程才开始持续读取对端返回的消息。直到对端也返回 CNXN,连接才真正算建立成功。
也就是说,Socket 连上了,不代表 ADB 连上了;CNXN 对上了,才算协议握手进入下一步。
AUTH 不是密码,是 token 签名
如果设备还没有信任当前应用的密钥,就会返回 AUTH。
这里最容易被误解的一点是:它不是“输个密码登录”,而是“对 token 做签名”。
源码里第一次收到 token 时,会走类似这样的逻辑:
crypto.signAdbTokenPayload(msg.getPayLoad())签名成功后,再发送 AUTH_TYPE_SIGNATURE。设备拿自己认识的公钥验证这个签名,确认是不是同一把私钥对应的 host key。
如果设备还不认识这把 key,代码会继续发送 RSA 公钥,触发设备端授权。

OPEN 打开 shell stream
握手通过后,TerminalEntrance 会调用:
mConnection.open("shell:")这一步才是真正打开一个 shell stream。
OPEN 不是“执行命令”,它只是把一个可写的流打开。等对端回 OKAY 之后,这条 stream 才真正进入可写状态。
所以你在终端页里看到能输入命令,不是因为已经有一个 Socket,而是因为 ADB stream 已经完成了 OPEN -> OKAY 的确认。
WRTE 和 OKAY 负责流控
ADB stream 不是想写就一直写。
WRTE 负责传数据,OKAY 负责告诉对方可以继续发。
在 ADB WiFi 里,这套逻辑被放进了连接线程和 stream 的状态管理中:
• 本端写出去时,先等 writeReady• 对端回 OKAY后,恢复可写• 对端发 WRTE时,把 payload 放进读队列• 然后回 OKAY,告诉对方继续
这就是这套协议里最实用的地方:它不是单次请求,而是一条持续流动的数据管道。

终端页为什么能稳定收发
终端页面实际上有两条业务线程在跑:
1. 写线程从命令队列里取输入,给命令补换行后写入 stream 2. 读线程不断从 stream 里取输出,再把内容交给界面显示
也就是说,界面上看到的实时输出,不是 UI 自己“猜出来的”,而是连接线程收到 WRTE 后,先把数据放进队列,再由读线程消费出来。
这样设计的好处很明显:状态清楚,关闭也干净。
关闭同样重要
当对端发送 CLSE,或者本端主动关闭时,项目会清理对应的 stream,通知等待中的读写线程退出。
这部分看起来没有握手那么“刺激”,但它决定了工具能不能稳定用。
无线调试、USB 拔出、连接失败、页面退出,这些场景都需要让等待中的线程醒过来,而不是卡死。
最后
所以,ADB WiFi 这条链路可以浓缩成一句话:
先把物理通道统一成 AdbChannel,再用 24 字节消息头承载 CNXN、AUTH、OPEN、WRTE、OKAY 和 CLSE。
理解了这条线,再回头看内置终端,你会发现它不是“发一条命令”这么简单,而是在手机应用里实现了一小段 host 端 ADB 客户端能力。
如果你平时也在做 Android 开发、测试或自动化,这条协议链路值得自己走一遍。
夜雨聆风