Skip to content

ADB架构

追踪 adb client、host server、transport、smart socket 与 adbd 如何协作完成一次 adb shell 请求,并解释 packet 复用、认证、断线和失败清理。

基于android-17.0.0_r1
AndroidADBadbd调试桥传输协议

ADB架构 ​

执行 adb shell id 时,终端里只有一条命令,但 ADB 实际上要跨越开发机上的 client、server、设备 上的 adbd、USB 或 TCP transport、多个 socket 和两套协议。把 ADB 简化为“电脑通过 USB 连接手机” 会掩盖最容易出错的地方:命令首先进入哪个进程,设备选择什么时候发生,shell 服务何时创建,数据怎样 在一条物理连接上复用,以及断线后谁负责关闭状态。

本文面向已经读过 模块化编译、 编译错误排查 和 系统镜像结构 的读者。你应当知道如何运行一个 ADB 命令和查看构建产物, 但不需要预先了解 atransport、asocket 或 apacket。本文只建立架构和一条完整调用链;shell 参数、 文件同步、端口转发、无线配对和安装部署分别留给本模块后续专题。

读完后,应该能回答:adb 为什么会自动启动 server,host:tport:any 和 shell:id 分别在哪里被 处理,A_OPEN 的两个 id 如何对应两端 socket,以及设备拔出时为什么不会留下一个假在线的 shell。

1. 进程连接 ​

1.1 client ​

Android 17 的 ADB 源码仍然把 host client 和 host server 编译在同一个 adb 可执行文件中,但它们 是不同的进程角色:

角色运行位置入口主要拥有的状态直接消费者
client开发机client/main.cpp::main → adb_commandline命令参数、目标选择、与 server 的 fd终端、脚本、DDMLIB
server开发机后台进程client/main.cpp::adb_server_mainsmart socket、设备扫描、transport 列表、多路复用client 与设备 transport
adbdAndroid 设备或模拟器daemon/main.cpp::main设备端 transport、认证、shell/sync/jdwp 服务shell 子进程和调试服务

client 和 server 的“同一个二进制”不是“同一个进程”。client 通过 server 的 smart socket 发送服务 请求;server 再决定请求是在 host 内部完成,还是沿某个 transport 转到 adbd。

1.2 adb ​

随 AOSP packages/modules/adb 一起维护的上游 ADB 总览文档 把 server 描述为负责设备发现、状态维护和数据复用的中心循环;上游 ADB 内部实现说明 进一步说明 server 一侧暴露 client 连接,另一侧通过 transport 监视多个 adbd。这些是 AOSP 上游仓库 中的开发文档,不是本站内部页面。因此 server 至少拥有这些全局事实:

  • 当前有哪些 USB、emulator 或网络 transport;
  • 每个 transport 的 serial、product、model、device、feature 和连接状态;
  • 哪个 client socket 已选择哪个 transport;
  • 哪些 local/remote asocket 互为 peer;
  • 哪些 listener、forward/reverse 和 tracker 需要在断线时清理。

adbd 不负责开发机上的设备列表,也不会替 client 决定连接哪个 serial。它只在自己的 transport 上 接受 ADB packet,认证成功后根据 A_OPEN 的地址创建设备端服务。

1.3 transport抽象 ​

transport.h 中的 TransportType 当前至少包括:

源码文件:packages/modules/adb/transport.h

相关函数/类型:TransportType

cpp
enum TransportType {
    kTransportUsb,
    kTransportLocal,
    kTransportAny,
    kTransportHost,
};

kTransportHost 是特殊值,表示服务由 server 自己提供,并不对应真实设备 transport。USB 和 local transport 都被抽象成 atransport,所以上层的 adb shell 不需要知道字节最终通过 USB bulk endpoint 还是 emulator TCP socket。

2. Client入口 ​

2.1 Client命令 ​

client/main.cpp::main 只做追踪初始化并调用 adb_commandline。后者解析 -s serial、-t transport-id、 -d、-e、-H、-P、-L 等参数,随后计算 server socket spec。默认 server 是开发机本地 tcp:localhost:5037;环境变量和显式 -H/-P/-L 可以改变它。

目标选择先留在 client 进程的静态状态中:

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

相关函数/类型:adb_set_transport

cpp
static TransportType __adb_transport = kTransportAny;
static const char* __adb_serial = nullptr;
static TransportId __adb_transport_id = 0;

void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
    __adb_transport = type;
    __adb_serial = serial;
    __adb_transport_id = transport_id;
}

这些变量只记录 client 这次请求希望选择的目标,真正的 atransport 对象属于 server。把它们理解 成“client 已经打开了设备”会混淆两个进程的所有权。

2.2 adb_connect ​

adb_connect 会先进入 adb_check_server_version。它向 server 发送 host:version;server 返回四位 十六进制版本字符串。当前 adb.h 中的 ADB_SERVER_VERSION 为 41。

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

相关函数/类型:__adb_check_server_version

cpp
unique_fd fd(_adb_connect("host:version", nullptr, error));

if (fd == -2 && local) {
    fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
    if (launch_server(__adb_server_socket_spec, one_device)) {
        *error = "cannot connect to daemon";
        return false;
    }
} else {
    if (version != ADB_SERVER_VERSION) {
        adb_kill_server();
        goto start_server;
    }
}

远程 server 不能由本地 client 自动 fork,因此连接失败时不会盲目启动远程进程;本地 server 版本不匹配时才进入 kill/restart 路径。这解释了为什么 ADB_SERVER_SOCKET=tcp:host:port 的失败 恢复能力不同于默认本地 5037。

2.3 launch_server ​

launch_server fork/exec 同一个 adb 二进制,并传入 fork-server、--reply-fd 等参数。子进程进入 adb_server_main 后先初始化 signal、日志、重连 handler、mDNS 和 USB/emulator 扫描,再安装 disabled 的 smart socket listener,等待设备扫描完成后通过 ack pipe 写入 OK\n,最后在 fdevent loop 上调用 enable_server_sockets。

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

相关函数/类型:adb_server_main

cpp
while (install_listener(socket_spec, kSmartSocketConnectTo, nullptr,
                        INSTALL_LISTENER_DISABLED, nullptr, &error) != INSTALL_STATUS_OK) {
    if (std::chrono::steady_clock::now() - start > 0.5s) {
        LOG(FATAL) << "could not install *smartsocket* listener: " << error;
    }
    std::this_thread::sleep_for(100ms);
}

std::thread notify_thread([ack_reply_fd]() {
    adb_wait_for_device_initialization();
    // ...向父进程报告 OK...
    fdevent_run_on_looper([] { enable_server_sockets(); });
});

disabled listener 与 ack pipe 共同避免 client 看到“server 进程已存在、设备列表却还没初始化”的 中间状态。server 的 owner 是 fdevent loop;扫描工作不能阻塞它处理启动完成回调。

3. socket路由 ​

3.1 长度协议 ​

ADB smart socket 的请求格式是四个 ASCII 十六进制字符加 payload。例如查询 server 版本时是:

text
000Chost:version

adb_client.cpp::_adb_connect 负责连接 socket、可选地发送 transport 选择请求,再发送目标 service:

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

相关函数/类型:_adb_connect

cpp
if (!service.starts_with("host") || force_switch) {
    std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
    if (!transport_result) return -1;
}

if (!SendProtocolString(fd.get(), service)) {
    *error = perror_str("write failure during connection");
    return -1;
}

if (!adb_status(fd.get(), error)) {
    return -1;
}

SendProtocolString 写入长度前缀和正文;adb_status 只消费四字节 OKAY 或 FAIL 加错误文本。成功 状态并不必然关闭连接:选择 transport 后,后续请求仍在同一个 client-server socket 上继续发送。

3.2 smart socket ​

server 的 smart_socket_enqueue 将收到的数据累积在 asocket::smart_socket_data,直到至少有四字节 长度且 payload 完整:

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

相关函数/类型:smart_socket_enqueue

cpp
if (s->smart_socket_data.size() < 4) {
    return 0;
}

uint32_t len = unhex(s->smart_socket_data.data(), 4);
if (len == 0 || len > MAX_PAYLOAD) {
    goto fail;
}

if ((len + 4) > s->smart_socket_data.size()) {
    return 0;
}

service = std::string_view(s->smart_socket_data).substr(4);

return 0 不是成功响应,而是“当前数据还不足以解析请求”。长度为 0、超过 MAX_PAYLOAD 或 解析失败才进入关闭路径。测试中的分片输入正是为了验证不能假设一次 read 就得到完整 service。

3.3 Socket路由 ​

收到完整 service 后,smart socket 先识别 host-serial:、host-transport-id:、host-usb:、 host-local:、host: 前缀。

请求形态处理 owner结果
host:version、host:devices、host:featureshandle_host_requestserver 直接返回数据
host:tport:any、host:tport:serial:<id>handle_host_requestserver 选择 atransport,返回 OKAY 和 transport id
track-devices、wait-for-devicehost_service_to_socketserver 创建 tracker 或等待线程 socket
shell:...、sync:...、jdwpconnect_to_remotesmart socket 把请求转成 transport 上的远端 stream

transport 选择的当前 Android 17 实现位于 adb.cpp::handle_host_request:

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

相关函数/类型:handle_host_request

cpp
if (service.starts_with("transport") || service.starts_with("tport:")) {
    TransportType type = kTransportAny;
    bool legacy = true;
    if (android::base::ConsumePrefix(&service, "tport:")) {
        legacy = false;
        if (android::base::ConsumePrefix(&service, "serial:")) {
            serial_storage = service;
            serial = serial_storage.c_str();
        } else if (service == "usb") {
            type = kTransportUsb;
        } else if (service == "local") {
            type = kTransportLocal;
        } else if (service == "any") {
            type = kTransportAny;
        }
    }

    atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
    if (t != nullptr) {
        s->transport = t;
        SendOkay(reply_fd);
        if (!legacy) WriteFdExactly(reply_fd, &t->id, sizeof(t->id));
        return HostRequestResult::SwitchedTransport;
    }
}

tport 不是设备端命令,而是 server 内部改变 smart socket 后续路由状态的 host service。只有 成功绑定 s->transport 后,后续不带 host: 前缀的 service 才能沿设备 transport 发送。

3.4 生命周期 ​

当 host service 创建了本地 socket,smart socket 会把原 client peer 重新变成普通 local socket,连接 到新服务 socket,发送 OKAY,然后关闭自己。对于远端 service,它则把 peer 设置为等待通知的 local socket,调用 connect_to_remote,再关闭 smart socket。

这就是“smart socket”的准确含义:它只负责第一次的 service 解析与路由选择,不承载整个 shell 数据流。 真正的数据通道由 local/remote asocket peer 负责。

4. Transport状态 ​

4.1 Transport状态 ​

USB 扫描器或 emulator/TCP 连接发现设备后,会创建 atransport 并注册到 server 的 transport 列表。 register_socket_transport 会先检查 pending/online 列表中是否已有同名 transport,再安排 fdevent loop 完成注册。设备未完成握手时状态仍是 offline/connecting,不能因为“发现了 USB”就对 client 报 device。

ConnectionState 包含连接阶段与设备运行模式:

源码文件:packages/modules/adb/transport.h

相关函数/类型:ConnectionState

cpp
enum ConnectionState {
    kCsConnecting,
    kCsAuthorizing,
    kCsUnauthorized,
    kCsNoPerm,
    kCsDetached,
    kCsOffline,
    kCsBootloader,
    kCsDevice,
    kCsHost,
    kCsRecovery,
    kCsSideload,
    kCsRescue,
};

ConnectionStateIsOnline 只把可承载 service 的状态视为 online。offline 表示对端已被发现但连接尚未 建立或已失去连接;unauthorized 表示认证尚未完成,不能把它当成普通离线设备。

4.2 A_CNXN ​

建立 USB/TCP 连接后,双方通过 ADB packet 的 A_CNXN 交换协议版本、最大 payload 和 banner。当前 A_VERSION 为 0x01000001,MAX_PAYLOAD 为 1 MiB,旧版本可能使用 4 KiB 上限。

adb.cpp::handle_new_connection 在 host 侧先 handle_offline,再调用 update_version、读取 banner、 parse_banner,最后 handle_online。这样旧的 product/model/features 不会残留到新连接上。

4.3 Transport失败 ​

在非 TLS 路径中,A_AUTH 的 token/signature/public-key 交换由 host client/auth.cpp 与 device daemon/auth.cpp 共同完成。设备端认证成功后才调用 adbd_auth_verified 并再次发送连接信息;失败 会增加 failed_auth_attempts,继续发送认证请求或保持 unauthorized。

这解释了 adb devices 中 unauthorized 的来源:设备 transport 已存在,但认证 owner 尚未把它提升 为 online。只重启 host server 不一定能解决设备侧未确认 RSA key 的问题。

4.4 断线清理 ​

atransport::HandleError 把错误切回 fdevent loop,调用 handle_offline 和 transport_destroy。 handle_offline 会把 state 改成 offline、清除 online 标志、调用 close_all_sockets(t) 和所有 disconnect 回调。

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

相关函数/类型:handle_offline

cpp
t->SetConnectionState(kCsOffline);
t->online = 0;
close_all_sockets(t);
t->RunDisconnects();

清理顺序保护了两个不变量:transport 断开后不能再向设备写 packet;所有依赖该 transport 的 local/remote socket 必须收到关闭,避免 client 永久等待一个不会再有数据的 fd。

5. packet ​

5.1 Packet结构 ​

对象所在层作用
amessagepacket 头command、两端 id、payload 长度、checksum、magic
apackettransport 数据单元amessage + payload,一次读写的协议单位
asocketstream 抽象维护 fd、peer、packet queue、transport 和流关闭状态

asocket 是单向的;双向服务由两个互为 peer 的 asocket 表示。host 侧的 local socket 连接 client fd, remote socket 连接 transport;device 侧相反。这样多个 shell、sync、JDWP 流可以在一个 USB/TCP transport 上并行存在,每个流用 local/remote id 对标识。

5.2 Packet服务 ​

当 server 已选择 transport,并要处理 shell:id 时,connect_to_remote 创建 remote socket 并发送:

text
A_OPEN(arg0 = host_local_id, arg1 = send_window, payload = "shell:id")

设备 handle_packet 收到 A_OPEN 后调用 create_local_service_socket(address, t);在 adbd 侧, daemon_service_to_socket 识别 shell 并调用 StartSubprocess,得到连接到 shell 子进程的 service socket。随后 adbd 发送 A_OKAY,携带设备端 remote id。

5.3 A_OKAY 流控 ​

旧协议中 A_OKAY 表示对端已准备好接收下一批数据;当前实现还可在 payload 中携带 delayed-ack 的 已消费字节数。local_socket_flush_incoming 在收到数据写入本地 fd 后,根据 transport 是否支持 delayed ack 决定发送窗口更新。

A_OPEN 的 arg1 不能随便当成设备端 id:在支持 delayed ack 的 transport 上它是初始发送窗口, 设备必须接受与 capability 一致的值;设备返回的 A_OKAY 才提供远端 id。

5.4 A_WRTE/A_CLSE ​

handle_packet 收到 A_WRTE 后用 p->msg.arg1 查找本地 socket,并用 p->msg.arg0 校验 peer;找到后 把 payload enqueue 到 local socket。A_CLSE 同样按两端 id 找到 socket,再关闭 peer。

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

相关函数/类型:handle_packet (A_WRTE/A_CLSE)

cpp
case A_CLSE:
    if (t->online && p->msg.arg1 != 0) {
        asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
        if (s) s->close(s);
    }
    break;

case A_WRTE:
    if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
        asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
        if (s) s->enqueue(s, std::move(p->payload));
    }
    break;

设备拔出后,旧 packet 可能晚于关闭事件抵达;t->online 和 peer id 检查共同阻止它把数据注入 已经关闭或属于其他 transport 的 socket。关闭逻辑还防止错误对端用 A_CLSE(0, remote_id) 关闭不属于 自己的 host stream。

6. adbshellid ​

6.1 shellservice ​

client/commandline.cpp::adb_shell 先读取 server/device feature,决定是否使用 shell protocol、PTY 或 raw 模式,再由 ShellServiceString 生成:

text
shell[,pty|raw][,shell protocol]:id

交互终端默认倾向 PTY;非交互输入可以使用 raw;设备不支持 shell_v2 时,client 会退回旧 shell 语义。 这些选择属于 shell 专题,但它们最终都被压缩成 ADB service 字符串交给 adb_connect。

6.2 请求阶段 ​

完整顺序是:

这里的 host:tport:any 是 Android 17 client/server 的新 transport 选择请求;旧文档中常见的 host:transport-any 是兼容旧协议的另一种拼法,不能在源码分析中混写。handle_host_request 会根据 tport: 分支返回选中的 TransportId,client 保存它以便后续请求和错误信息使用。

6.3 每一层消费者 ​

事件状态拥有者下一个消费者
client 解析 shell 参数adb_shelladb_connect
server 版本检查adb_clientadb_server_main 或已有 server
选择目标server atransport 列表smart socket 的 s->transport
连接握手atransportparse_banner、认证状态和 device tracker
创建流host/device 两端 asocketdaemon_service_to_socket
输出传递packet queue、fdevent、peerclient stdout/ShellProtocol
退出或断线transport + peerA_CLSE、fd 清理和 client 返回

如果只能说“ADB 把命令发给 adbd”,就还没有说明这些状态的 owner 和生效时机;真正调试时应沿表格 找到最后一个成功消费者。

7. 失败路径边界 ​

7.1 server级失败 ​

现象代码边界判断方式
cannot connect to daemon_adb_connect / launch_serverserver socket spec、端口占用、远程地址和启动日志
adb server version ... doesn't match__adb_check_server_versionhost:version 返回值与 ADB_SERVER_VERSION
远程 server 不自动启动__adb_check_server_versionis_local_socket_spec 为 false,需先启动远端 server
unknown host servicesmart_socket_enqueuehandle_host_request 与 host_service_to_socket 都未处理请求

adb kill-server 也不是杀掉设备 adbd;它发送 host:kill,由 host server 自己退出,设备 transport 随后因连接关闭而被清理。

7.2 设备失败 ​

adb devices 显示 offline、unauthorized、no permissions 或 bootloader 时,不应直接去看 shell 子进程。先判断:

bash
adb server-status
adb devices -l
adb get-state
adb get-serialno

这些命令本身通过 host service 或 tracker 工作。offline 表示 transport 尚未 online;unauthorized 表示认证 owner 尚未完成;bootloader 和 sideload 是另一种对端服务状态,不能当作普通 Android adbd。

7.3 断线失败 ​

即使 transport 显示 device,A_OPEN 仍可能因 shell: 格式、service 不存在、权限或设备资源失败。 handle_packet 在 create_local_service_socket 返回空时发送 A_CLSE,host client 只能看到连接被关闭。 这时应同时检查:

bash
adb -s <serial> shell getprop ro.build.version.release
adb -s <serial> shell id
adb -s <serial> shell 'logcat -b all -d | grep -i -E "adbd|shell|CANNOT LINK|avc: denied"'

不要把 adb shell 失败归为 USB 问题;如果 adb devices 已是 device,transport 层可能已经正常, 失败点更可能在 adbd service、子进程、SELinux 或 shell protocol。

7.4 断线和半关闭 ​

设备拔出时可能有三种数据尚未完成:host local socket 的 fd、transport 写队列、对端尚未确认的 apacket。handle_offline 先把 transport 标成 offline,再关闭所有关联 socket;socket 的 deferred close 还会尝试把已经写入的缓冲排空,避免直接 close 导致已成功写出的数据被 TCP RST 丢弃。

因此“终端没有立即返回”不一定是 server 死锁,可能是 socket 正在等待 flush/EOF。打开 host trace 时应同时观察 transport、sockets、packets 和 fdevent,不要只开一个 adb 日志标签。

8. 架构测试 ​

8.1 架构测试 ​

socket_test.cpp::test_parse_host_service 对空 service、普通 serial、带端口 serial、IPv6、tcp:、 udp: 和嵌入 NUL 的输入做断言。它证明 parse_host_service 的职责是从 service 前缀中分离 serial 和 command,而不是验证设备是否真的存在。

这一区分很重要:解析通过只代表路由字符串结构合法,后续 acquire_one_transport 仍可能返回“device not found”或 ambiguous。

8.2 Transport验证 ​

transport_test.cpp 构造 atransport,调用 parse_banner 后断言连接状态为 kCsHost,并检查 product/model/device 与 features。SetFeatures 测试还确认重复 feature 会被去重、替换和清空。

它反向证明 banner 是 transport 状态的输入,而 adb devices -l 输出的 product/model/device 并不是 从 USB 名称临时拼出来的。

8.3 Packet验证 ​

types_test.cpp 的 APacketReader 测试把多个 packet 合并到一个 block,再按 1~255 字节切碎后喂给 reader;reader 仍必须还原相同 packet 序列。另一个测试把 payload 设为 MAX_PAYLOAD + 1,要求返回 ERROR。

它证明 ADB 不能假设底层 read/write 保留 packet 边界,也证明 payload 上限是协议安全边界,而不是 终端命令长度建议。

8.4 Listener验证 ​

adb_listeners_test.cpp 覆盖普通安装、重绑定、禁止重绑定、动态端口、删除、删除不存在 listener 以及 transport disconnect。测试在 teardown 中调用 remove_all_listeners 并检查 fdevent 计数为零。

它证明 listener 状态拥有者是 server,并且 transport 断开会触发清理;不能把 forward/reverse 的 socket 状态留给 client 进程自行猜测。

9. 源码导航 ​

问题首个入口继续阅读
client 为什么启动/重启 serverclient/adb_client.cpp::adb_check_server_versionclient/commandline.cpp::launch_server、client/main.cpp::adb_server_main
请求如何选择设备client/adb_client.cpp::switch_socket_transportadb.cpp::handle_host_request、transport.cpp::acquire_one_transport
host service 为什么返回 unknownsockets.cpp::smart_socket_enqueueadb.cpp::handle_host_request、services.cpp::host_service_to_socket
设备为何 offline/unauthorizedadb.cpp::handle_new_connectiontransport.cpp、daemon/auth.cpp、client/auth.cpp
adb shell 怎样创建进程adb.cpp::handle_packet 的 A_OPENdaemon/services.cpp::daemon_service_to_socket、daemon/shell_service.cpp
输出为何卡住或丢失sockets.cpp::local_socket_flush_incoming/outgoingA_OKAY 窗口、fdevent、A_WRTE/A_CLSE
设备拔出后如何回收adb.cpp::handle_offlineclose_all_sockets、atransport::HandleError、disconnect callbacks

一个可靠的源码阅读顺序是:先读上游 ADB 总览文档 建立三组件边界,再读上游 ADB 内部实现说明 建立 smart socket、transport、asocket 和 fdevent 模型,最后沿 adb shell id 进入 adb_client.cpp、sockets.cpp、adb.cpp 与 daemon/services.cpp。

当你再次看到“ADB 不工作”时,不要从命令名猜原因。先问这四个问题:server socket 是否可达,目标 transport 是否 online,A_OPEN 是否创建了远端 service,最后一个 A_WRTE/A_CLSE 到达了哪一端。能把 这四个问题分别映射到当前 Android 17 的 owner、状态和测试,才是对 ADB 架构的可验证理解。