Skip to content

Zygote参数传递协议

对照 ZygoteProcess 编码和 ZygoteArguments 解析,解释参数顺序、重复限制、remaining args 和错误边界。

基于android-17.0.0_r1
AndroidZygote参数协议源码阅读

Zygote参数传递协议 ​

Zygote 参数协议是有顺序和类型约束的文本协议,不是任意 --key=value 集合。客户端先写参数数量,再逐行写字符串;服务端 ZygoteArguments.parseArgs() 按顺序识别控制参数,遇到第一个未知/非 option 参数后把剩余内容交给目标 Java main。关键安全字段禁止重复,--rlimit 和 compat changes 允许累积,管理命令禁止尾随 spawn 参数。

1. Wire framing ​

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

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

首行是 argc,之后必须恰好有 argc 行参数。参数中的 newline、carriage return、NUL 在发送前拒绝,因为它们会破坏 framing。响应由具体命令决定:spawn 返回 pid/usingWrapper,ABI/PID query 返回长度前缀字节,状态命令返回 int。

2. Spawn参数前缀 ​

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

源码注释要求 runtime/setuid/setgid/setgroups 位于前部。--runtime-args 是普通 spawn 命令的标志;parser 在需要 runtime args 却没见到它时拒绝请求。

3. 禁止重复参数 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteArguments.java

java
if (arg.startsWith("--setuid=")) {
    if (mUidSpecified) {
        throw new IllegalArgumentException(
                "Duplicate arg specified");
    }
    mUidSpecified = true;
    mUid = Integer.parseInt(
            getAssignmentValue(arg));
} else if (arg.startsWith("--setgid=")) {
    if (mGidSpecified) {
        throw new IllegalArgumentException(
                "Duplicate arg specified");
    }
    ...
}

uid、gid、target SDK、seInfo、capabilities、setgroups、invoke-with、nice-name、package-name 等关键字段禁止重复。安全审查注释明确这样做是为了降低参数注入和“后一个值覆盖前一个值”的歧义。

4. 允许累积参数 ​

java
} else if (arg.startsWith("--rlimit=")) {
    String[] values = getAssignmentList(arg);
    if (values.length != 3) {
        throw new IllegalArgumentException(
                "--rlimit= should have 3 comma-delimited ints");
    }
    if (mRLimits == null) {
        mRLimits = new ArrayList<>();
    }
    mRLimits.add(parseRlimit(values));
} else if (arg.startsWith(
        "--disabled-compat-changes=")) {
    mDisabledCompatChanges =
            parseAndMergeCompatChanges(
                    arg, mDisabledCompatChanges);
}

一个进程可以有多个 rlimit tuple;compat changes 也允许多次出现并合并、排序、去重,以支持多个调用层追加配置。空 compat 项被忽略,不使用 Set 以减少关键路径分配。

5. Mount与数据map ​

java
if (bindMountAppStorageDirs) {
    argsForZygote.add(
            Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
}
if (bindMountAppsData) {
    argsForZygote.add(
            Zygote.BIND_MOUNT_APP_DATA_DIRS);
}

argsForZygote.add(
        Zygote.PKG_DATA_INFO_MAP + "="
                + encodedPackageVolumeInodeTriples);

mount 采用布尔 flag,data info map 使用逗号列表编码 package、volume UUID、inode 三元组。parser 只拆分字符串,结构长度和 inode 语义还要由 native isolation 代码校验。

6. processClass分界 ​

java
argsForZygote.add(processClass);
Collections.addAll(argsForZygote, extraArgs);
argsForZygote.add(
        "--use-deliqueue=" + useDeliQueue);

parser 遇到第一个未识别 option/普通字符串后停止读取 Zygote 控制参数,将它和后续内容放入 mRemainingArgs。对普通应用,第一个 remaining arg 是 android.app.ActivityThread,后面的 seq=、deli queue 由 ActivityThread.main 消费。

7. 管理命令独占性 ​

java
if (mBootCompleted) {
    if (argCount > curArg) {
        throw new IllegalArgumentException(
                "Unexpected arguments after --boot-completed");
    }
} else if (mAbiListQuery || mPidQuery) {
    if (argCount > curArg) {
        throw new IllegalArgumentException(
                "Unexpected arguments after --query-abi-list.");
    }
} else if (mPreloadApp != null) {
    if (argCount > curArg) {
        throw new IllegalArgumentException(
                "Unexpected arguments after --preload-app.");
    }
}

ABI/PID/boot/preload-app 等 standalone 命令不能混入 spawn 参数,避免一个请求同时改变 Zygote 全局状态和 fork child。API denylist 命令则显式消费所有剩余参数。

8. Child Zygote约束 ​

java
if (mStartChildZygote) {
    boolean seenChildSocketArg = false;
    for (String arg : mRemainingArgs) {
        if (arg.startsWith(
                Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
            seenChildSocketArg = true;
            break;
        }
    }
    if (!seenChildSocketArg) {
        throw new IllegalArgumentException(
                "--start-child-zygote specified without socket name");
    }
}

child zygote 必须在 remaining args 中包含随机/专用 socket 名,否则新 Zygote 没有命令通道。accepted ABI list 和 UID range 同样由 child zygote 参数消费者解析。

9. 错误与导航 ​

  • argc 与实际行数不一致:command buffer EOF/解析失败;
  • 数字格式错误:Integer/Long parse 异常;
  • 重复安全字段:拒绝整个请求;
  • 缺 --runtime-args:普通 spawn 被拒绝;
  • processClass 前后顺序错误:Zygote 控制参数变成应用参数或相反;
  • child zygote 缺 socket:拒绝启动;
  • data map 结构异常:可能在 native isolation 阶段失败。
bash
# 客户端编码顺序和条件参数。
rg -n "startViaZygote|argsForZygote|runtime-args|setuid|setgroups|PKG_DATA_INFO_MAP|disabled-compat-changes|processClass|use-deliqueue" \
  frameworks/base/core/java/android/os/ZygoteProcess.java

# 服务端 parser、重复限制、管理命令和 remaining args。
rg -n "parseArgs|Duplicate arg|expectRuntimeArgs|seenRuntimeArgs|mRemainingArgs|Unexpected arguments|mStartChildZygote|parseAndMergeCompatChanges" \
  frameworks/base/core/java/com/android/internal/os/ZygoteArguments.java

下一篇将分析 child PID 在 Zygote、SystemServer 和 ProcessRecord 之间的流转;不会重复参数解析规则。