Skip to content

ZygoteProcess.start

拆解 startViaZygote 参数编码、ABI socket 选择、USAP 回退、argc 行协议和 pid/wrapper 响应。

基于android-17.0.0_r1
AndroidZygoteProcessUSAP源码阅读

ZygoteProcess.start ​

SS033 解释了 AMS 如何决定启动参数;本篇只讲 ZygoteProcess 怎样把这些字段变成 Zygote wire protocol。它先同步 USAP pool 开关,startViaZygote() 按固定顺序构造字符串参数,在 mLock 内选择支持目标 ABI 的 ZygoteState,再把 argc + 每行一个参数 写入普通 Zygote 或 USAP socket,读取 pid 和 wrapper 标志。

1. start入口 ​

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

java
public final Process.ProcessStartResult start(
        String processClass, String niceName,
        int uid, int gid, int[] gids,
        int runtimeFlags, int mountExternal,
        int targetSdkVersion, String seInfo,
        String abi, String instructionSet,
        String appDataDir, String invokeWith,
        String packageName, int zygotePolicyFlags,
        boolean isTopApp, ... ) {
    if (fetchUsapPoolEnabledPropWithMinInterval()) {
        informZygotesOfUsapPoolStatus();
    }
    try {
        return startViaZygote(...);
    } catch (ZygoteStartFailedEx ex) {
        throw new RuntimeException(
                "Starting VM process through Zygote failed", ex);
    }
}

USAP 配置属性按最小间隔刷新,变化时通知 primary/secondary Zygote;随后才开始本次进程命令。内部失败统一包装为 RuntimeException 返回给 ProcessList。

2. 固定参数前缀 ​

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

java
ArrayList<String> argsForZygote = new ArrayList<>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
argsForZygote.add(
        "--target-sdk-version=" + targetSdkVersion);

if (gids != null && gids.length > 0) {
    argsForZygote.add(
            "--setgroups=" + commaSeparatedGids);
}

源码注释要求 runtime/setuid/setgid/setgroups 位于前部,ZygoteArguments parser 依赖该协议。mountExternal 根据枚举转成不同 flag;nice name、seInfo、instruction set、app data dir、invoke-with 等按非空条件加入。

3. 隔离与兼容参数 ​

java
if (pkgDataInfoMap != null
        && !pkgDataInfoMap.isEmpty()) {
    argsForZygote.add(
            Zygote.PKG_DATA_INFO_MAP + "="
                    + encodeDataInfo(pkgDataInfoMap));
}
if (bindMountAppStorageDirs) {
    argsForZygote.add(
            Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
}
if (bindMountAppsData) {
    argsForZygote.add(
            Zygote.BIND_MOUNT_APP_DATA_DIRS);
}
if (disabledCompatChanges != null
        && disabledCompatChanges.length > 0) {
    argsForZygote.add(
            "--disabled-compat-changes="
                    + join(disabledCompatChanges));
}

data info map 被编码为 package、volume UUID、inode 三元组序列;mount flag 只表达是否执行绑定。compat change 数组以逗号列表传输,接收端在 specialization 后写入 VMRuntime/AppCompatCallbacks。

4. 入口类与额外参数 ​

java
argsForZygote.add(processClass);
if (extraArgs != null) {
    Collections.addAll(argsForZygote, extraArgs);
}

argsForZygote.add(
        "--use-deliqueue=" + useDeliQueue);

参数顺序具有语义:processClass 之前由 Zygote parser 消费,之后的参数传给 ActivityThread 等目标 main。start sequence 和 deli queue 参数位于目标类之后,最终由应用入口读取。把 Zygote 控制 flag 放在 processClass 后会变成应用参数而失效。

5. ABI连接与锁 ​

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

mLock 保护 primary/secondary ZygoteState、output writer 和 input stream,确保多个启动请求不会把请求/响应字节交叉。openZygoteSocketIfNeeded() 先尝试 primary ABI,再尝试 secondary;返回的 state 是长连接缓存。

6. wire format校验 ​

java
for (String arg : args) {
    if (arg.indexOf('\n') >= 0) {
        throw new ZygoteStartFailedEx(
                "Embedded newlines not allowed");
    } else if (arg.indexOf('\r') >= 0) {
        throw new ZygoteStartFailedEx(
                "Embedded carriage returns not allowed");
    } else if (arg.indexOf('\u0000') >= 0) {
        throw new ZygoteStartFailedEx(
                "Embedded nulls not allowed");
    }
}

String msgStr = args.size() + "\n"
        + String.join("\n", args) + "\n";

协议是首行参数数量,后续每行一个参数。换行、回车和 NUL 会破坏 framing,因此写 socket 前全部拒绝,避免向 Zygote 留下半个请求。

7. USAP与普通Zygote ​

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

java
if (shouldAttemptUsapLaunch(
        zygotePolicyFlags, args)) {
    try {
        return attemptUsapSendArgsAndGetResult(
                zygoteState, msgStr);
    } catch (IOException ex) {
        Log.e(LOG_TAG,
                "IO Exception while communicating with USAP pool");
    }
}
return attemptZygoteSendArgsAndGetResult(
        zygoteState, msgStr);

只有 policy/参数允许时才尝试 USAP;USAP socket I/O 失败会回退普通 Zygote,而 USAP 返回负 pid 表示 specialization 失败并抛异常。USAP 不能用于需要 wrapper 的进程,所以返回 usingWrapper=false。

8. PID与wrapper响应 ​

java
zygoteWriter.write(msgStr);
zygoteWriter.flush();

Process.ProcessStartResult result =
        new Process.ProcessStartResult();
result.pid = zygoteInputStream.readInt();
result.usingWrapper =
        zygoteInputStream.readBoolean();

if (result.pid < 0) {
    throw new ZygoteStartFailedEx("fork() failed");
}

客户端总是读取完整 pid+boolean,避免残留字节污染下一次启动。普通 I/O 异常会关闭 ZygoteState,下一次请求重新连接;负 pid 转成 fork failure。成功返回只表示 Zygote 已给出 child pid,不表示应用已 attach AMS。

9. 失败与导航 ​

  • 参数含协议字符:写 socket 前失败;
  • ABI 不支持:连接选择失败;
  • USAP I/O 失败:回退普通 Zygote;
  • USAP specialization 负 pid:启动失败,不回退重复 fork;
  • 普通 socket I/O 失败:关闭缓存 state 并抛异常;
  • pid 成功但应用未 attach:进入 AMS attach timeout/子进程崩溃路径。
bash
# 参数编码、processClass 分界和 mount/compat flags。
rg -n "startViaZygote|argsForZygote|PKG_DATA_INFO_MAP|disabled-compat-changes|processClass|use-deliqueue" \
  frameworks/base/core/java/android/os/ZygoteProcess.java

# ABI 连接、wire format、USAP 回退和 pid 响应。
rg -n "openZygoteSocketIfNeeded|zygoteSendArgsAndGetResult|attemptUsapSendArgsAndGetResult|attemptZygoteSendArgsAndGetResult|Embedded newlines|readBoolean" \
  frameworks/base/core/java/android/os/ZygoteProcess.java

下一篇将进入 USAP 池本身:池大小、预 fork、specialization、reporting pipe 和 refill;不会重复本篇的通用命令协议。