Skip to content

ADB网络与转发

解释 forward、reverse、传统 TCP ADB 与 TLS Wi-Fi ADB 的连接模型、状态所有者、失败路径和验证方法。

基于android-17.0.0_r1
AndroidADBforwardreverseWi-Fi调试mDNS

ADB网络与转发 ​

adb forward tcp:9000 tcp:8080 和 adb connect 192.0.2.10:5555 都会让开发机与设备之间出现一条“网络路径”,但它们解决的是不同问题:前者保留现有 ADB transport,只在 transport 内建立一个服务转发;后者改变的是 ADB transport 本身,让 host 通过 TCP 或 TLS 连接 adbd。

这一区分很重要。端口转发失败时,通常要检查 listener、socket spec 和目标服务;网络 ADB 失败时,则要检查 transport 是否建立、认证是否完成以及网络发现是否可达。把两者混为“端口没开”会把排查带到错误的进程。

前置阅读是 ADB架构 和 ADB shell机制。前者介绍 adb client、server、transport 和 adbd 的边界,后者介绍一个已建立 transport 上的 service 如何运行;本文从 packages/modules/adb 的真实入口把同一边界扩展到 listener 和网络 transport。需要继续验证 transport 上的应用安装事务时,可阅读 ADB安装与权限管理。

1. 建立四个模型 ​

1.1 forward ​

bash
adb forward tcp:9000 tcp:8080

左侧是开发机上的本地端点,右侧是设备端服务。成功后,开发机进程连接 127.0.0.1:9000,ADB server 接受连接,再通过选中的 transport 请求设备连接 tcp:8080。

forward 不会把 adbd 变成网络监听器,也不会改变设备的 ADB 连接方式。它的 owner 是 host ADB server 中的 alistener;listener 的 target 是设备服务名。

1.2 reverse ​

bash
adb reverse tcp:8080 tcp:9000

这次设备端监听 tcp:8080。设备上的应用连接该端口后,连接经过 ADB transport 回到开发机,再由 host 连接 tcp:9000。命令仍由 host client 发起,但 listener 的实际安装请求通过 adbd 的 reverse_service 进入另一侧。

1.3 传统 TCP ADB ​

bash
adb tcpip 5555
adb connect 192.0.2.10:5555

adb tcpip 是一个发给当前设备的 tcpip: service 请求。设备端 adbd 设置 TCP 端口并重启自身的 TCP service。之后 adb connect 才会让 host server 创建一个以网络地址为 serial 的 TCP transport。这里没有 host listener 把某个应用端口映射到设备;ADB 协议本身直接跑在 TCP 上。

1.4 TLSWi-FiADB ​

较新的无线调试流程通常是:

bash
adb pair 192.0.2.10:37099 123456
adb connect 192.0.2.10:41567

配对端口负责建立信任关系,连接端口负责建立 TLS ADB transport。adb pair 成功后,host 将设备 GUID 和证书材料保存到 adb_known_hosts.pb;mDNS 发现的是可安全连接的服务,最终由 TLS client 与设备侧 TlsServer 建立 socket。配对成功不等于当前 transport 已在线。

四种操作改变的对象不同

本文中的“网络 ADB”包含传统明文 TCP ADB 和 TLS Wi-Fi ADB。两者都改变 transport,但只有后者引入证书配对、known-host 和 mDNS secure-connect 服务。

2. 转发命令 ​

2.1 client协议 ​

Android 17 的 client/commandline.cpp 在 adb_commandline() 中解析 forward 参数。以固定端口为例:

text
adb forward tcp:9000 tcp:8080
        │       │        │
        │       │        └─ remote socket spec
        │       └────────── local socket spec
        └────────────────── host command

随后 client 将请求编码为类似下面的 host service:

text
host:forward:tcp:9000;tcp:8080

当使用 --no-rebind 时,编码中会出现 forward:norebind:;--list、--remove 和 --remove-all 则分别进入 list-forward、killforward: 和 killforward-all 形式。client 本身不创建监听 fd,真正的 bind 和 listener 表由 server 侧处理。

adb reverse 的编码不同:

text
reverse:forward:tcp:8080;tcp:9000

这个前缀会沿当前 transport 发到 adbd,因此不能依据命令都在 host 终端中输入,就推断 listener 一定在 host。

2.2 forward request ​

adb.cpp::handle_forward_request() 接收解析后的 forward:、list-forward、killforward: 和 killforward-all 请求。创建转发时,它依次完成三件事:

  1. 解析 local;remote 两个 socket spec,并检查 local/remote 的合法组合;
  2. 根据 serial 或 transport 选择目标设备;
  3. 调用 install_listener() 在 host listener 表中创建或替换条目。

每个 alistener 至少记录本地监听 socket、local_name、connect_to 和关联的 atransport。因此 adb forward --list 展示的不是“设备打开了哪些端口”,而是 server 当前拥有的 host-side listener 状态。

源码文件:packages/modules/adb/adb.cpp

相关函数/类型:handle_forward_request

cpp
if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
    std::string error;
    atransport* transport = transport_acquirer(&error);
    if (!transport) {
        SendFail(reply_fd, error);
        return true;
    }

    bool kill_forward = false;
    bool no_rebind = false;
    if (android::base::StartsWith(service, "killforward:")) {
        kill_forward = true;
        service += 12;
    } else {
        service += 8;
        if (android::base::StartsWith(service, "norebind:")) {
            no_rebind = true;
            service += 9;
        }
    }
    std::vector<std::string> pieces = android::base::Split(service, ";");
    // ... 校验 killforward 的单字段格式或 forward 的 local;remote 格式
    InstallStatus r;
    int resolved_tcp_port = 0;
    if (kill_forward) {
        r = remove_listener(pieces[0].c_str(), transport);
    } else {
        int flags = no_rebind ? INSTALL_LISTENER_NO_REBIND : 0;
        r = install_listener(pieces[0], pieces[1].c_str(), transport, flags,
                             &resolved_tcp_port, &error);
    }
    if (r == INSTALL_STATUS_OK) {
        SendOkay(reply_fd);
        if (resolved_tcp_port != 0) {
            SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
        }
        return true;
    }
    // ... 根据 InstallStatus 发送具体失败文本
}

transport_acquirer 先决定这条映射归属哪个 transport,install_listener 才拥有 listener 表和本地 fd。 因此 OKAY 的生效时机是 listener 安装成功,而不是远端服务已经接受连接;tcp:0 的实际端口也在这一层 通过协议返回给 client。

2.3 动态端口 ​

tcp:0 只适用于让系统为 local endpoint 选择空闲端口:

bash
adb forward tcp:0 tcp:8080

install_listener() 完成 bind 后,server 从 socket 得到实际端口,handle_forward_request() 将解析后的端口返回给 client。脚本应读取命令输出,而不是假定端口永远是某个固定值。remote tcp:0 没有同样语义,因为设备服务端无法把“任意空闲端口”解释成一个已知目标。

Android 17 的 adb_utils_test.cpp::test_forward_targets_are_valid 对这一边界做了反向验证:local tcp:0 合法,remote tcp:0 不合法,负数、超范围端口和非数字端口都会被拒绝。

3. 生命周期 ​

3.1 install_listener ​

install_listener() 成功只说明 host 能够创建本地 listener,并不保证设备上的 tcp:8080 已经有服务。真正的远端连接要等到某个进程连接 local endpoint 后才开始。

连接到 tcp:9000 后,adb_listeners.cpp::listener_event_func() 在 listener fd 上收到可读事件,执行大致如下的路径:

text
accept(local listening fd)
    -> create_local_socket(accepted fd)
    -> local socket transport = listener transport
    -> connect_to_remote(connect_to)
    -> A_OPEN(remote service)

host 的 accepted fd 被包装成 asocket,并继承 listener 选择的 atransport。connect_to_remote() 再把目标 socket spec 交给 ADB service 层,设备端最终通过 service_to_fd() 或对应服务工厂得到文件描述符。

3.2 Packet状态 ​

对应用来说,local endpoint 看起来像普通 TCP;对 transport 来说,数据被封装在 ADB 的 A_OPEN、A_WRTE、A_OKAY 和 A_CLSE packet 中。设备服务关闭 fd 时,关闭消息沿 transport 回到 host;host 应用关闭 accepted fd 时,local asocket 发送关闭请求到设备端。

这解释了一个常见现象:设备服务是否监听,不能用 host 上的 ss -lnt 判断;host 只会看到 9000,设备端的 8080 由 adbd 在连接建立后按 service spec 打开。

3.3 三种失败点 ​

失败阶段典型表现owner应检查什么
bind 阶段cannot bind listenerhost serverlocal 地址、端口占用、权限、socket spec
transport 阶段device offline、连接被关闭server/transportserial 选择、认证、USB/TCP/TLS transport 状态
remote service 阶段local 端口可连但立即 EOFadbd/serviceremote spec、目标服务是否存在、设备权限和服务退出码

不要把“命令返回 OKAY”当作端到端成功。创建 listener 的成功确认早于第一次 accepted connection,也早于设备服务打开。

4. rebind ​

4.1 adb forward ​

当同一个 transport 上已有相同 local endpoint 时,普通 adb forward 允许 rebind。实现会找到旧 listener,关闭其 fd,再安装新的 target。这样可以方便脚本反复启动,但也可能无意中覆盖一个正在使用的映射。

源码文件:packages/modules/adb/adb_listeners.cpp

相关函数/类型:install_listener

cpp
for (auto& l : listener_list) {
    if (local_name == l->local_name) {
        if (flags & INSTALL_LISTENER_NO_REBIND) {
            *error = "cannot rebind";
            return INSTALL_STATUS_CANNOT_REBIND;
        }
        l->connect_to = connect_to;
        if (l->transport != transport) {
            l->transport->RemoveDisconnect(&l->disconnect);
            l->transport = transport;
            l->transport->AddDisconnect(&l->disconnect);
        }
        return INSTALL_STATUS_OK;
    }
}

auto listener = std::make_unique<alistener>(local_name, connect_to);
listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
if (listener->fd < 0) return INSTALL_STATUS_CANNOT_BIND;

这段代码把两类失败分开:已有 endpoint 且禁止替换时返回 CANNOT_REBIND;新 endpoint 无法 bind 时返回 CANNOT_BIND。前者保留旧映射,后者没有可用 listener。源码中的 listener_list_mutex 还保护了查找、替换 和新增的临界区,所以排查时不能只看端口是否空闲,还要看是否已有同名 listener。

4.2 --no-rebind ​

bash
adb forward --no-rebind tcp:9000 tcp:8080

client 发送 forward:norebind:,handle_forward_request() 将 no_rebind 传给 install_listener()。如果 local endpoint 已存在,命令失败而旧映射保持不变。Android 17 的 adb_listeners_test.cpp::test_install_listener_no_rebind 直接验证了这一点;测试的证明边界是 listener manager 的行为,不覆盖 shell 环境中的端口竞争。

4.3 remove ​

bash
adb forward --remove tcp:9000
adb forward --remove-all
adb forward --list

remove 按 local endpoint 删除单个 listener,remove-all 删除当前 transport 的全部 forward。adb reverse --remove 和 adb reverse --remove-all 走对应的 reverse service。删除 listener 只释放转发状态,不会停止远端服务本身;远端服务是否退出取决于它自己的 fd 生命周期。

4.4 清理 ​

listener_disconnect() 遍历 listener 表,移除关联目标 transport 的条目并关闭本地 fd。这个清理动作避免设备拔出后 host 仍显示一个可连接但永远失败的端口。test_transport_disconnect 和 remove-all 测试覆盖了这条生命周期边界。

5. adb reverse ​

5.1 命令到达adbd ​

执行:

bash
adb reverse tcp:8080 tcp:9000

client 发送 reverse:forward:tcp:8080;tcp:9000。server 先通过当前 transport 把 reverse: 请求交给 adbd。daemon/services.cpp::reverse_service() 为这个请求创建 socketpair,然后把请求委托给通用的 handle_forward_request()。socketpair 让设备侧服务可以复用 ADB 的 socket/transport 连接代码,而不必另造一套数据通道。

设备侧 listener 的 local endpoint 是 tcp:8080,其 target 是 host endpoint tcp:9000。当设备应用连接 8080,adbd 接受连接并沿 transport 发起 host-side open;host 收到后再连接本机 9000。

5.2 reverse 边界 ​

reverse 需要设备上的 adbd 支持对应 service,并且设备端有权限 bind 指定地址。它不能绕过应用沙箱或网络防火墙;它只是让设备侧的 socket listener 把连接交给 ADB transport。adb reverse --list 查询的是 reverse listener 状态,不是设备上所有监听端口。

6. socket spec ​

ADB 的 forward/reverse endpoint 不是只接受 tcp:<port>。packages/modules/adb/socket_spec.cpp 和 socket_spec_test.cpp 定义并验证多种地址:

spec典型使用listener/consumer
tcp:9000TCP 端口socket_spec_listen 或 connect
localabstract:nameAndroid abstract UNIX socketadbd/设备本地服务
localfilesystem:path文件系统 UNIX socket需要路径权限的本地服务
jdwp:<pid>Java 调试器入口adbd 的 JDWP service
vsock:<cid>:<port>虚拟机/宿主通信emulator 或受支持设备
dev:/path设备文件需要对应 fd 权限
acceptfd:<fd>复用已有 fdhost 侧调用者提供

其中 tcp: 的 listen 和 connect 行为由同一套 socket spec 解析器分流。socket_spec_listen_connect_tcp 测试验证了创建 TCP listener、连接它以及传输数据;malformed spec 测试则证明解析器会拒绝缺少参数或格式不完整的地址。

学习源码时应追踪三个问题:谁解析 spec,谁拥有被创建的 fd,谁消费连接后的字节。比如 localabstract: 的 fd 在设备侧由 adbd 打开,而 tcp: local endpoint 的 fd 在 host server 中创建;这两个 endpoint 名字都出现在一条 forward 命令里,但 owner 不同。

7. 传统TCP ADB ​

7.1 adbtcpipPORT ​

bash
adb tcpip 5555

client 将端口编码为 tcpip:5555,通过当前 USB 或其他已在线 transport 发送给 adbd。daemon/services.cpp 的 tcpip: 分支校验端口后调用 restart_tcp_service()。

daemon/restart_service.cpp::restart_tcp_service() 设置 service.adb.tcp.port,然后触发 adbd 重启,使新的 adbd 在 TCP 端口上接受 ADB protocol。它不是在 host server 里“把 USB 映射到 5555”,而是改变设备端 adbd 的监听方式。

传统 TCP ADB 的网络与认证边界

传统 TCP ADB 依赖目标网络可达,并且 Android 产品可能禁用或限制该入口。端口能够连接只说明 TCP 建立,不代表 ADB authentication、transport online 和 shell service 都成功。

7.2 adbconnect ​

client/commandline.cpp 将 adb connect 转成 host:connect:<address>。adb.cpp::handle_host_request() 解析地址并查找已有 transport;没有匹配项时创建 TCP transport,已有但失效的 transport 则可能被 kick 后重建。ParseNetAddress() 负责 IPv4、主机名和带端口地址的基本分解。

adb disconnect host:port 走同一地址解析路径,找到匹配 transport 后调用 kick。disconnect 不会修改设备端应用,也不会删除 forward listener;但 transport 断开会触发 listener 清理,所以相关 forward 可能随之消失。

8. TLS Wi-Fi ADB ​

8.1 配对信任 ​

client/adb_wifi.cpp::adb_wifi_pair_device() 负责 host 侧配对。它创建证书,启动 PairingClient::Start(),向设备配对端口发送 pairing code,并等待对等端响应。成功后 KnownWifiHostsFile::AddKnownHost() 将设备 GUID 等信息写入 adb_known_hosts.pb。

这个文件不是“当前在线设备列表”,而是后续 TLS 连接用的 known-host 数据。错误密码、配对服务停止监听或 feature 不支持都会让配对失败;pairing tests 对这些情况有覆盖。

8.2 TlsServer ​

在 adbd 侧,daemon/adb_wifi.cpp::TlsServer::Start() 创建 TLS listener,选择动态端口并初始化证书上下文。register_adb_tls_service() 向 mDNS 注册安全调试服务,adbd_send_tls_server_port() 将端口报告给 framework lifecycle,兼容路径下也可通过旧 property 传递。

当 TlsServer::OnFdEvent() 接受新 socket 后,TLS 握手和认证完成,代码调用 register_socket_transport() 把这个 fd 纳入 adbd 的 transport 管理。此后它与 USB transport 共享 ADB packet、认证和 service 分发逻辑;区别只在底层 socket 和 TLS 会话。

8.3 TLS边界 ​

daemon/mdns.cpp 注册和注销 ADB TLS 服务。host 侧 services 层提供 list-mdns-known-hosts、track-mdns-services 等请求,adb_wifi_secure_connect() 根据服务名和 known-host 证书建立安全连接。

因此排查 Wi-Fi ADB 时要分开记录:

  1. 配对端口是否可达、pairing code 是否正确;
  2. known-host 文件是否保存成功;
  3. mDNS 服务是否可见,或是否可以直接使用 HOST:PORT;
  4. TLS 握手是否完成并注册为 online transport。
配对为何不等于连接

配对建立的是“以后允许谁连接”的信任关系;连接建立的是“现在用于 ADB packet 的 transport”。设备可以已经配对但暂时没有可发现的 secure-connect 服务,也可以 mDNS 已发现但 TLS 认证失败。

8.4 TLS失败 ​

症状可能停留的阶段代码边界
pairing code 错误pairing client 等待错误响应PairingClient 返回失败,不写入有效 known host
adb pair 成功但列表无设备配对完成,secure-connect 未发现mDNS 注册、网络隔离或服务生命周期
adb connect 显示 unauthorized/offlinesocket 已建立,认证或 transport 初始化未完成host transport 状态机
TLS 握手失败socket 可达但证书验证失败TlsServer/Wi-Fi pairing connection
连接后很快消失transport 断开server/adbd 清理 transport 和 listener

不要用“能 ping 通”证明 ADB 可用。ICMP 可达不能证明配对端口、TLS 端口、mDNS multicast 或 ADB authentication 均可用。

9. 转发测试 ​

9.1 Listener验证 ​

adb_listeners_test.cpp 的测试输入和断言可以整理为:

测试输入断言证明边界
test_install_listener未占用 local speclistener 安装成功manager 能建立基本状态
test_install_listener_rebind同一 local spec 二次安装旧 target 被替换默认 rebind 语义
test_install_listener_no_rebind重复安装并禁止 rebind返回错误且旧条目保留不覆盖真实端口竞争
test_install_listener_tcp_port_0local tcp:0获得非零实际端口动态端口解析
test_transport_disconnect断开关联 transportlistener 被移除清理关联关系

9.2 Socket验证 ​

adb_utils_test.cpp::test_forward_targets_are_valid 适合验证格式与端口范围,不证明防火墙或设备服务状态。socket_spec_test.cpp::socket_spec_listen_connect_tcp 证明 TCP endpoint 的 listen/connect 组合可以工作,但不等于 USB/TLS transport 已建立。

pairing_connection_test.cpp 覆盖成功配对、错误密码、server 停止监听和 feature 支持。它验证的是 pairing library 的状态转换和错误处理;完整设备 Wi-Fi 调试仍需要 framework lifecycle、mDNS 网络和 adbd TLS server 一起参与。

这就是阅读测试时应保留的边界:测试断言越具体,能支持的源码结论越具体;不能把一个 host 单元测试扩大解释成所有产品、所有网络环境都可用。

10. 故障分层 ​

10.1 关系类型 ​

目标首先看什么不要先看什么
host 访问设备服务forward listener、remote servicemDNS
设备访问 host 服务reverse listener、host targetadb tcpip
host 连接网络 adbdTCP/TLS transport、认证forward 列表
Wi-Fi 自动发现pairing、known host、mDNS、TLS设备应用端口

10.2 forward/reverse ​

  1. 用 adb forward --list 或 adb reverse --list 确认 listener 是否存在;
  2. 检查 local/remote spec 是否写反,尤其是 tcp:0 只能作为 local 动态端口;
  3. 确认目标 transport 是当前选择的 serial,且状态为 online;
  4. 让实际消费者连接 local endpoint,观察失败发生在 accept 之前还是 remote service 打开之后;
  5. transport 断开后重新查询 listener,因为断线清理可能已经移除旧状态。

10.3 tcpip: ​

  1. 传统 TCP 模式先确认 tcpip: 请求成功,再确认网络地址和端口可达;
  2. TLS Wi-Fi 模式先确认 pairing,再确认 known-host 和 secure-connect 服务;
  3. mDNS 不可用时,用已知 HOST:PORT 区分“发现失败”和“连接/认证失败”;
  4. 看到 offline 或 unauthorized 时,停在 transport/authentication 层,不要继续排查 shell 命令;
  5. transport online 后,才用 adb shell、adb forward 等 service 验证上层功能。

故障报告应保留的最小上下文

故障报告最好同时记录命令、serial、local/remote spec、transport 状态和首次失败阶段。只记录“端口不通”无法判断是 host bind、ADB packet、设备 service、TLS 认证还是 mDNS 发现的问题。

11. 连接状态 ​

可以把本文的四种操作压缩成下面这张图:

forward 和 reverse 的核心状态是 listener;tcpip 和 connect 的核心状态是 transport;pair 和 TLS Wi-Fi 的核心状态是 trust material、发现服务与安全 transport。它们最后都可以消费 shell:、sync: 或其他 ADB service,但进入 service 层之前的 owner 和失败边界完全不同。

理解这个分层后,命令的选择就不再依靠记忆:需要把一个设备服务暴露给 host,用 forward;需要让设备应用访问 host 服务,用 reverse;需要让 ADB 自己脱离 USB,用 TCP 或 TLS Wi-Fi;需要建立 Wi-Fi 调试信任,先 pair,再 connect。

12. 入口对应 ​

初学者最容易在多个同名函数之间迷路。下面按一次故障中可能观察到的现象,列出应进入的 Android 17 源码位置:

现象或问题首个入口继续追踪的对象你要确认的事实
adb forward 参数被拒绝client/commandline.cpp::adb_commandlineadb_utils_test.cpp::test_forward_targets_are_validclient 是否生成了合法 service,端口是否在允许范围
命令成功但 --list 没有预期项adb.cpp::handle_forward_requestformat_listeners、alistener选择的 transport 和 listener owner 是否相同
local 端口能连接但立即关闭adb_listeners.cpp::listener_event_funcconnect_to_remote、A_OPENaccepted fd 是否创建,remote spec 是否被设备端消费
reverse 安装失败daemon/services.cpp::reverse_serviceadbd 侧 handle_forward_request请求是否到达 adbd,设备 listener 是否完成 bind
adb tcpip 返回成功但 connect 失败daemon/restart_service.cpprestart_tcp_service、host handle_host_requestadbd 是否重启到 TCP,host 是否创建了新 transport
Wi-Fi 设备不出现在列表client/adb_wifi.cppKnownWifiHostsFile、mDNS service tracker配对材料、服务发现和 secure-connect 是否分别成功
TLS socket 建立后变 offlinedaemon/adb_wifi.cppTlsServer::OnFdEvent、register_socket_transportTLS 会话是否真正注册为 ADB transport

这种定位方式体现了“入口、owner、消费者”的关系:adb_commandline 消费 argv,handle_forward_request 消费 service 字符串,alistener 消费本地连接事件,adbd service 消费 ADB open 请求,最终的应用或调试工具才消费字节流。只阅读命令帮助无法得到这条所有权链。

12.1 观察实验 ​

可以在设备上启动一个明确监听 TCP 的服务,然后把它映射到 host:

bash
adb forward tcp:0 tcp:8080
adb forward --list

第一条命令的输出端口是 server 在 bind 后解析出的结果。第二条命令应该显示 local endpoint、serial 和 remote endpoint。随后让 host 客户端连接这个动态端口,才能触发 listener_event_func() 和远端 A_OPEN;只执行第一条命令不会触发设备服务连接。

如果第二条命令没有条目,问题在 listener 安装或查询路径;如果有条目但连接后 EOF,问题已经越过 host bind,应该转向 remote service 和设备端权限。这个实验把“安装成功”和“端到端服务成功”明确分成两个时间点。

12.2 消费者 ​

reverse 的消费者在设备侧,因此验证顺序相反:

bash
adb reverse tcp:8080 tcp:9000
adb reverse --list

然后让设备应用或设备 shell 中的客户端连接 127.0.0.1:8080。host 上的 9000 服务只有在设备连接发生后才会看到新连接。若 host 服务没有收到连接,不要先修改 host 服务协议;应先确认设备端 listener 是否存在、应用是否真的连接了同一个地址族和端口。

12.3 状态证据 ​

执行 adb connect 后,adb devices -l 中的 serial 通常是 HOST:PORT 或由 mDNS 解析出的服务名。后续的 adb forward、adb shell 都会选择这个 transport。若同时存在 USB 和 Wi-Fi 设备,必须使用 -s SERIAL 验证,否则你可能把对 USB transport 的观察误认为是 Wi-Fi transport 的结果。

bash
adb devices -l
adb -s 192.0.2.10:5555 shell getprop ro.build.version.release
adb -s 192.0.2.10:5555 forward --list

这三个命令分别观察 transport 列表、service 消费者和 listener 状态。它们的共同前提是 transport 已 online;如果状态是 offline 或 unauthorized,应回到认证和连接层,而不是继续改变 forward 目标。

13. 混淆点 ​

第一,forward 不是网络 ADB。 forward 复用已存在的 ADB transport;即使 local endpoint 使用 TCP,ADB server 与设备之间仍然可以是 USB。网络 ADB 则要求 host 能直接建立 TCP/TLS transport。

第二,reverse 不是把 host 端口“推送”到设备文件系统。 reverse 安装的是设备侧 listener,设备应用产生连接后,ADB transport 才把连接事件带回 host。

第三,adb tcpip 不是 adb forward tcp:5555 ... 的别名。 前者请求 adbd 改变自身监听和重启方式,后者只在 host 侧增加一个应用服务映射。

第四,配对不是连接。 pairing 产生可验证的身份材料,connect 才创建当前可用的 ADB transport。mDNS 是发现机制,不是权限授予机制。

第五,listener 表不是设备端口表。 --list 只反映当前 ADB 实例管理的 forward/reverse 条目;它不能替代设备上的 ss、应用服务状态或 framework 的 Wi-Fi 调试状态。

把这五个结论分别放回代码 owner,就能避免大多数“命令看起来成功但业务不通”的误判。