Skip to content

LocalSocket与Zygote通信

追踪 ZygoteState、LocalSocket、accept、peer credentials、ABI握手、命令流和连接重试。

基于android-17.0.0_r1
AndroidZygoteLocalSocket源码阅读

LocalSocket与Zygote通信 ​

Zygote 通信使用 Unix domain LocalSocket:init 创建并命名监听 socket,ZygoteServer 包装 fd 并 accept,客户端 ZygoteProcess 建立 session socket 后先查询 ABI,再发送 spawn/preload 命令。连接的 owner、权限和关闭责任分布在三层:init/内核持有监听资源,ZygoteServer 持有服务端 wrapper,ZygoteState 持有客户端读写流。

1. 客户端状态 ​

源码文件:frameworks/base/core/java/android/os/ZygoteProcess.java

java
private static class ZygoteState implements AutoCloseable {
    final LocalSocketAddress mZygoteSocketAddress;
    final LocalSocketAddress mUsapSocketAddress;
    private final LocalSocket mZygoteSessionSocket;
    final DataInputStream mZygoteInputStream;
    final BufferedWriter mZygoteOutputWriter;
    private final List<String> mAbiList;
    private boolean mClosed;
}

ZygoteState 缓存一次连接的地址、session socket、输入/输出流和服务端 ABI list。mLock 保护多个线程对 writer/input 的串行访问,避免一条命令的字节与另一条命令交叉。

2. 建连与回滚 ​

java
static ZygoteState connect(
        LocalSocketAddress zygoteSocketAddress,
        LocalSocketAddress usapSocketAddress)
        throws IOException {
    LocalSocket socket = new LocalSocket();
    if (zygoteSocketAddress == null) {
        throw new IllegalArgumentException(
                "zygoteSocketAddress can't be null");
    }
    try {
        socket.connect(zygoteSocketAddress);
        DataInputStream input = new DataInputStream(
                socket.getInputStream());
        BufferedWriter output = new BufferedWriter(
                new OutputStreamWriter(
                        socket.getOutputStream()),
                Zygote.SOCKET_BUFFER_SIZE);
        return new ZygoteState(...,
                input, output,
                getAbiList(output, input));
    } catch (IOException ex) {
        socket.close();
        throw ex;
    }
}

连接、流创建或 ABI 握手任一步失败,临时 socket 都会关闭;不会把半初始化 state 放入 primary/secondary 缓存。成功的 ZygoteState 由 attemptConnectionToPrimaryZygote() 或 secondary 对象持有。连接构造中的 getAbiList() 随即在同一 session 上完成下面的握手。

3. ABI握手 ​

java
writer.write("1");
writer.newLine();
writer.write("--query-abi-list");
writer.newLine();
writer.flush();

int numBytes = inputStream.readInt();
byte[] bytes = new byte[numBytes];
inputStream.readFully(bytes);
String rawList = new String(
        bytes, StandardCharsets.US_ASCII);
return Arrays.asList(rawList.split(","));

握手本身也使用 Zygote 文本命令:参数数量 1、命令名 --query-abi-list;响应是 int 长度加 ASCII ABI 列表。客户端保存列表后,后续 matches(abi) 只做本地判断,不再每次启动重复查询。

4. 服务端接入 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java、frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

java
private ZygoteConnection acceptCommandPeer(
        String abiList) {
    try {
        return createNewConnection(
                mZygoteSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex);
    }
}

accept 只完成服务端 fd 接收;新连接构造时还必须读取 Unix peer credentials 并应用 Zygote 的 system UID 门槛。

java
ZygoteConnection(LocalSocket socket,
        String abiList) throws IOException {
    mSocket = socket;
    peer = mSocket.getPeerCredentials();
    if (peer.getUid() != Process.SYSTEM_UID) {
        throw new ZygoteSecurityException(
                "Only system UID is allowed to connect to Zygote.");
    }
}

accept 返回的是每次客户端连接的 session socket,构造 ZygoteConnection 时立即读取 peer credentials,仅允许 system UID。主监听 socket继续由 ZygoteServer持有;权限失败只影响当前连接。

5. 命令连接生命周期 ​

session socket是双向流:客户端写命令、服务端返回 pid/status。服务端在 processCommand() 中识别 EOF;客户端在 IOException 时关闭缓存 state,让下一次启动重新 connect/handshake。

6. 命令发送与阻塞 ​

java
synchronized (mLock) {
    ZygoteState state =
            openZygoteSocketIfNeeded(abi);
    return zygoteSendArgsAndGetResult(
            state, zygotePolicyFlags,
            argsForZygote);
}

同一 ZygoteState 的 writer/input 必须在锁内成对使用:写完整请求、flush、读取完整响应。connect 本身可能阻塞,ABI 不匹配时还会顺序尝试 secondary;因此锁粒度覆盖整个请求,保证 pid 响应与调用方一一对应。

7. 关闭与重试 ​

java
public void close() {
    try {
        mZygoteSessionSocket.close();
    } catch (IOException ex) {
        Log.e(LOG_TAG,
                "I/O exception on routine close", ex);
    }
    mClosed = true;
}

ZygoteState.close 只关闭客户端 session 并标记 closed;下一次 attemptConnectionToPrimaryZygote() 发现 closed 后重新连接。启动时等待 Zygote 的辅助方法按 500ms 间隔重试,到达超时后记录 WTF;它只验证 socket 可连接,不发送 spawn 命令。

8. init fd边界 ​

SS005 已说明 init 创建的 socket fd 通过环境变量进入 Zygote。ZygoteServer 的 mCloseSocketFd 区分 wrapper 是否拥有底层 fd;由 init 提供的 fd 在子进程关闭时需要 wrapper close 加显式 Os.close,避免 LocalSocket wrapper 不拥有原始 fd 导致泄漏。客户端 session socket 则由 LocalSocket 自身创建和关闭。

9. 失败定位与导航 ​

  • connect 失败:检查 socket name/namespace、init service 和 Zygote 是否监听;
  • ABI 握手失败:检查服务端响应长度、ASCII 列表和 stream 是否残留;
  • peer UID 拒绝:检查调用进程 credentials,不是 ABI 问题;
  • 命令写入阻塞:检查 mLock 持有者、服务端 poll 和 session 消费;
  • I/O 后重复使用旧 state:检查 ZygoteState.close() 和缓存重建;
  • 子进程 fd 泄漏:检查 init fd wrapper、mCloseSocketFd 和显式 Os.close。
bash
# 客户端连接、ABI握手、锁和关闭。
rg -n "class ZygoteState|connect\(|getAbiList|openZygoteSocketIfNeeded|mLock|close\(\)|waitForConnectionToZygote" \
  frameworks/base/core/java/android/os/ZygoteProcess.java

# 服务端 accept、peer credentials、命令连接和 socket close。
rg -n "acceptCommandPeer|createNewConnection|PeerCredentials|SYSTEM_UID|processCommand|closeServerSocket" \
  frameworks/base/core/java/com/android/internal/os/ZygoteServer.java \
  frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

下一篇将分析 ZygoteConnection.processCommand() 的参数解析、权限策略和 fork 命令处理;不会重复 LocalSocket 建连和 ABI 握手。