“This is the 25th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Analyze the application process startup process

In the previous startup process of the application Launcher, we analyzed the preorder of the startup process of the application Launcher. Next, in this article, we will analyze the startup process of the application process. We’ll continue to focus our analysis on the Launcher application process.

In the previous article, we analyzed that the application Process Launcher will eventually call the startProcess function of processList. Java file, and finally start the Process through the start function of process. Java file.

public static ProcessStartResult start(@NonNull final String processClass,
                                       @Nullable final String niceName,
                                       int uid, int gid, @Nullable int[] gids,
                                       int runtimeFlags,
                                       int mountExternal,
                                       int targetSdkVersion,
                                       @Nullable String seInfo,
                                       @NonNull String abi,
                                       @Nullable String instructionSet,
                                       @Nullable String appDataDir,
                                       @Nullable String invokeWith,
                                       @Nullable String packageName,
                                       int zygotePolicyFlags,
                                       boolean isTopApp,
                                       @Nullable long[] disabledCompatChanges,
                                       @Nullable Map<String, Pair<String, Long>>
                                               pkgDataInfoMap,
                                       @Nullable Map<String, Pair<String, Long>>
                                               whitelistedDataInfoMap,
                                       boolean bindMountAppsData,
                                       boolean bindMountAppStorageDirs,
                                       @Nullable String[] zygoteArgs) {
    return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, invokeWith, packageName,
                zygotePolicyFlags, isTopApp, disabledCompatChanges,
                pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                bindMountAppStorageDirs, zygoteArgs);
}
Copy the code

Of course, as we comb through the process, we need to sort out its incoming parameters

// Note: The parameter information here is obtained through log printing and code tracingThe first parameter, processClass, is defined in the startProcessLocked function in the processList.java file and has a value of"android.app.ActivityThread"The second parameter niceName is the name of the process you want to start. In this case, it is the name of the application process Launcher"com.android.launcher3"The third, fourth and fifth parameters, The sixth parameter runtimeFlags is the flags of the runtime. The seventh parameter mountExternal is the eighth parameter targetSdkVersion is the SDK version The ninth parameter seInfo is information about the selinux permissions that the process has. The tenth parameter ABI is information about the system abi. Here the eleventh parameter instructionSet isnullThe 12th parameter appDataDir is the address where the corresponding data is stored and the 13th parameter invokeWith isnullThe fourteenth parameter packageName is the name of the package to start the application. In this parameter, it is the name of the application process"com.android.launcher3"The fifteenth parameter zygotePolicyFlags is the process. ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE that is passed in when AMS calls startProcess. The sixteenth parameter isTopApp isfalseThe 17th parameter disabledCompatChanges indicates the information in the application. The 18th parameter pkgDataInfoMap indicates the MAP object of the corresponding application. Here is the launcher3 application information. WhitelistedDataInfoMap is the corresponding empty data. BindMountAppsData istrueThe twenty-first parameter bindMountAppStorageDirs isfalseThe twenty-second argument zygoteArgs indicates startReq=20
Copy the code

After that, the start function of ZYGOTE_PROCESS is called

// From the name, we also know that this must be related to ZYGOTE process
public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();

public final Process.ProcessStartResult start(@NonNull final String processClass,
                                              final String niceName,
                                              int uid, int gid, @Nullable int[] gids,
                                              int runtimeFlags, int mountExternal,
                                              int targetSdkVersion,
                                              @Nullable String seInfo,
                                              @NonNull String abi,
                                              @Nullable String instructionSet,
                                              @Nullable String appDataDir,
                                              @Nullable String invokeWith,
                                              @Nullable String packageName,
                                              int zygotePolicyFlags,
                                              boolean isTopApp,
                                              @Nullable long[] disabledCompatChanges,
                                              @Nullable Map<String, Pair<String, Long>>
                                                      pkgDataInfoMap,
                                              @Nullable Map<String, Pair<String, Long>>
                                                      whitelistedDataInfoMap,
                                              boolean bindMountAppsData,
                                              boolean bindMountAppStorageDirs,
                                              @Nullable String[] zygoteArgs) {
    / /...

    try {
        return startViaZygote(processClass, niceName, uid, gid, gids,
                runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
                pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                bindMountAppStorageDirs, zygoteArgs);
    }
    / /... catch exception log delete
}

private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
                                                  @Nullable final String niceName,
                                                  final int uid, final int gid,
                                                  @Nullable final int[] gids,
                                                  int runtimeFlags, int mountExternal,
                                                  int targetSdkVersion,
                                                  @Nullable String seInfo,
                                                  @NonNull String abi,
                                                  @Nullable String instructionSet,
                                                  @Nullable String appDataDir,
                                                  @Nullable String invokeWith,
                                                  boolean startChildZygote,
                                                  @Nullable String packageName,
                                                  int zygotePolicyFlags,
                                                  boolean isTopApp,
                                                  @Nullable long[] disabledCompatChanges,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          pkgDataInfoMap,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          whitelistedDataInfoMap,
                                                  boolean bindMountAppsData,
                                                  boolean bindMountAppStorageDirs,
                                                  @Nullable String[] extraArgs)
                                                  throws ZygoteStartFailedEx {
    / /... The argsForZygote parameter of the Process that needs to be started is configured here. The argsForZygote parameter is generated
    synchronized(mLock) {
        // The USAP pool can not be used if the application will not use the systems graphics
        // driver. If that driver is requested use the Zygote application start path.
        / / in the end, with zygote by zygoteSendArgsAndGetResult function to communication, here is how to realize the correspondence with zygote?
        returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), zygotePolicyFlags, argsForZygote); }}Copy the code

Through the above code shows that in the end, will pass the zygoteSendArgsAndGetResult function to correspondence with zygote, specific how to communication, we need to look at its first parameter openZygoteSocketIfNeeded (abi)

Open the communication link with Zygote

How does openZygoteSocketIfNeeded open the zygoTE communication link

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
    try {
        // Try to connect to primary zygote
        attemptConnectionToPrimaryZygote();

        if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }

        / /...
    }
    / /...
}

// Try to connect to the corresponding primary zygote socket
@GuardedBy("mLock")
private void attemptConnectionToPrimaryZygote(a) throws IOException {
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        // Socket communication connection
        primaryZygoteState =
                ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);

        maybeSetApiBlacklistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); }}// mZygoteSocketAddress is a socket link address named zygote
mZygoteSocketAddress = new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
public static final String PRIMARY_SOCKET_NAME = "zygote";

// Return the corresponding ZygoteState object after connecting with the mZygoteSocketAddress link address
static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
        @Nullable LocalSocketAddress usapSocketAddress)
        throws IOException {
    / /...
    try {
        // Connect to the socket server
        zygoteSessionSocket.connect(zygoteSocketAddress);
        zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
        // Write and pass socket data
        zygoteOutputWriter =
                new BufferedWriter(new OutputStreamWriter(zygoteSessionSocket.getOutputStream()), Zygote.SOCKET_BUFFER_SIZE);
    }
    // Close socket communication in catch exception
    Initialize the ZygoteState object
    return new ZygoteState(zygoteSocketAddress, usapSocketAddress, zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter, getAbiList(zygoteOutputWriter, zygoteInputStream));
}
Copy the code

A connection is established with a socket named zygote. A socket named Zygote is used in the configuration file.

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    ......
    socket zygote stream 660 root system
    ......
Copy the code

During parsing the configuration file, the socket server is initialized and waiting for a connection

Here, we will be directly connected to the Zygote server as a server. After successful connection, we will be able to communicate directly

The corresponding process is split by Zygote

By zygoteSendArgsAndGetResult function

private Process.ProcessStartResult zygoteSendArgsAndGetResult(
        ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
        throws ZygoteStartFailedEx {
    / /... Parameter nulling operation code omitted

    // Parameter redefinition
    String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
    / /... Function independent code omitted
    // Continue calling the function
    return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
}

private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult( ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
    try {
        final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
        final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
        // Write the corresponding argument
        zygoteWriter.write(msgStr);
        zygoteWriter.flush();

        // Get the result after the ZYGOTE process is finished
        Process.ProcessStartResult result = new Process.ProcessStartResult();
        result.pid = zygoteInputStream.readInt();
        result.usingWrapper = zygoteInputStream.readBoolean();
        if (result.pid < 0) {
            throw new ZygoteStartFailedEx("fork() failed");
        }

        return result;
    }
    / /... catch exception code delete
}
Copy the code

The above code is passed to the Zygote server side through the corresponding parameters, and then the corresponding running results are obtained from the server side, including the corresponding initialization process PID and usingWrapper parameters