/** Finish remaining work for the newly forked system server process. */
  private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
      throws ZygoteInit.MethodAndArgsCaller {

    closeServerSocket();

    // set umask to 0077 so new files and directories will default to owner-only permissions.
    FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);

    if (parsedArgs.niceName != null) {
      Process.setArgV0(parsedArgs.niceName);
    }

    if (parsedArgs.invokeWith != null) {
      WrapperInit.execApplication(
          parsedArgs.invokeWith,
          parsedArgs.niceName,
          parsedArgs.targetSdkVersion,
          null,
          parsedArgs.remainingArgs);
    } else {
      /*
       * Pass the remaining arguments to SystemServer.
       */
      RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
    }

    /* should never reach here */
  }
  /** Finish remaining work for the newly forked system server process. */
  private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
      throws ZygoteInit.MethodAndArgsCaller {

    closeServerSocket();

    // set umask to 0077 so new files and directories will default to owner-only permissions.
    Os.umask(S_IRWXG | S_IRWXO);

    if (parsedArgs.niceName != null) {
      Process.setArgV0(parsedArgs.niceName);
    }

    final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    if (systemServerClasspath != null) {
      performSystemServerDexOpt(systemServerClasspath);
    }

    if (parsedArgs.invokeWith != null) {
      String[] args = parsedArgs.remainingArgs;
      // If we have a non-null system server class path, we'll have to duplicate the
      // existing arguments and append the classpath to it. ART will handle the classpath
      // correctly when we exec a new process.
      if (systemServerClasspath != null) {
        String[] amendedArgs = new String[args.length + 2];
        amendedArgs[0] = "-cp";
        amendedArgs[1] = systemServerClasspath;
        System.arraycopy(
            parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
      }

      WrapperInit.execApplication(
          parsedArgs.invokeWith,
          parsedArgs.niceName,
          parsedArgs.targetSdkVersion,
          VMRuntime.getCurrentInstructionSet(),
          null,
          args);
    } else {
      ClassLoader cl = null;
      if (systemServerClasspath != null) {
        cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
        Thread.currentThread().setContextClassLoader(cl);
      }

      /*
       * Pass the remaining arguments to SystemServer.
       */
      RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    }

    /* should never reach here */
  }
  /**
   * Handles post-fork setup of child proc, closing sockets as appropriate, reopen stdio as
   * appropriate, and ultimately throwing MethodAndArgsCaller if successful or returning if failed.
   *
   * @param parsedArgs non-null; zygote args
   * @param descriptors null-ok; new file descriptors for stdio if available.
   * @param pipeFd null-ok; pipe for communication back to Zygote.
   * @param newStderr null-ok; stream to use for stderr until stdio is reopened.
   * @throws ZygoteInit.MethodAndArgsCaller on success to trampoline to code that invokes static
   *     main.
   */
  private void handleChildProc(
      Arguments parsedArgs,
      FileDescriptor[] descriptors,
      FileDescriptor pipeFd,
      PrintStream newStderr)
      throws ZygoteInit.MethodAndArgsCaller {

    /*
     * Close the socket, unless we're in "peer wait" mode, in which
     * case it's used to track the liveness of this process.
     */

    if (parsedArgs.peerWait) {
      try {
        ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
        sPeerWaitSocket = mSocket;
      } catch (IOException ex) {
        Log.e(TAG, "Zygote Child: error setting peer wait " + "socket to be close-on-exec", ex);
      }
    } else {
      closeSocket();
      ZygoteInit.closeServerSocket();
    }

    if (descriptors != null) {
      try {
        ZygoteInit.reopenStdio(descriptors[0], descriptors[1], descriptors[2]);

        for (FileDescriptor fd : descriptors) {
          IoUtils.closeQuietly(fd);
        }
        newStderr = System.err;
      } catch (IOException ex) {
        Log.e(TAG, "Error reopening stdio", ex);
      }
    }

    if (parsedArgs.niceName != null) {
      Process.setArgV0(parsedArgs.niceName);
    }

    if (parsedArgs.runtimeInit) {
      if (parsedArgs.invokeWith != null) {
        WrapperInit.execApplication(
            parsedArgs.invokeWith,
            parsedArgs.niceName,
            parsedArgs.targetSdkVersion,
            pipeFd,
            parsedArgs.remainingArgs);
      } else {
        RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
      }
    } else {
      String className;
      try {
        className = parsedArgs.remainingArgs[0];
      } catch (ArrayIndexOutOfBoundsException ex) {
        logAndPrintError(newStderr, "Missing required class name argument", null);
        return;
      }

      String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
      System.arraycopy(parsedArgs.remainingArgs, 1, mainArgs, 0, mainArgs.length);

      if (parsedArgs.invokeWith != null) {
        WrapperInit.execStandalone(
            parsedArgs.invokeWith, parsedArgs.classpath, className, mainArgs);
      } else {
        ClassLoader cloader;
        if (parsedArgs.classpath != null) {
          cloader = new PathClassLoader(parsedArgs.classpath, ClassLoader.getSystemClassLoader());
        } else {
          cloader = ClassLoader.getSystemClassLoader();
        }

        try {
          ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
        } catch (RuntimeException ex) {
          logAndPrintError(newStderr, "Error starting.", ex);
        }
      }
    }
  }