/**
   * Runs the zygote in accept-and-fork mode. In this mode, each peer gets its own zygote spawner
   * process. This code is retained for reference only.
   *
   * @throws MethodAndArgsCaller in a child process when a main() should be executed.
   */
  private static void runForkMode() throws MethodAndArgsCaller {
    while (true) {
      ZygoteConnection peer = acceptCommandPeer();

      int pid;

      pid = Zygote.fork();

      if (pid == 0) {
        // The child process should handle the peer requests

        // The child does not accept any more connections
        try {
          sServerSocket.close();
        } catch (IOException ex) {
          Log.e(TAG, "Zygote Child: error closing sockets", ex);
        } finally {
          sServerSocket = null;
        }

        peer.run();
        break;
      } else if (pid > 0) {
        peer.closeSocket();
      } else {
        throw new RuntimeException("Error invoking fork()");
      }
    }
  }
  /** Prepare the arguments and fork for the system server process. */
  private static boolean startSystemServer(String abiList, String socketName)
      throws MethodAndArgsCaller, RuntimeException {
    long capabilities =
        posixCapabilitiesAsBits(
            OsConstants.CAP_BLOCK_SUSPEND,
            OsConstants.CAP_KILL,
            OsConstants.CAP_NET_ADMIN,
            OsConstants.CAP_NET_BIND_SERVICE,
            OsConstants.CAP_NET_BROADCAST,
            OsConstants.CAP_NET_RAW,
            OsConstants.CAP_SYS_MODULE,
            OsConstants.CAP_SYS_NICE,
            OsConstants.CAP_SYS_RESOURCE,
            OsConstants.CAP_SYS_TIME,
            OsConstants.CAP_SYS_TTY_CONFIG);
    /* Hardcoded command line to start the system server */
    String args[] = {
      "--setuid=1000",
      "--setgid=1000",
      "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009",
      "--capabilities=" + capabilities + "," + capabilities,
      "--nice-name=system_server",
      "--runtime-args",
      "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
      parsedArgs = new ZygoteConnection.Arguments(args);
      ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
      ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

      /* Request to fork the system server process */
      pid =
          Zygote.forkSystemServer(
              parsedArgs.uid,
              parsedArgs.gid,
              parsedArgs.gids,
              parsedArgs.debugFlags,
              null,
              parsedArgs.permittedCapabilities,
              parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
      throw new RuntimeException(ex);
    }

    /* For child process */
    if (pid == 0) {
      if (hasSecondZygote(abiList)) {
        waitForSecondaryZygote(socketName);
      }

      handleSystemServerProcess(parsedArgs);
    }

    return true;
  }
  /**
   * Runs the zygote process's select loop. Accepts new connections as they happen, and reads
   * commands from connections one spawn-request's worth at a time.
   *
   * @throws MethodAndArgsCaller in a child process when a main() should be executed.
   */
  private static void runSelectLoopMode() throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList();
    ArrayList<ZygoteConnection> peers = new ArrayList();
    FileDescriptor[] fdArray = new FileDescriptor[4];

    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);

    int loopCount = GC_LOOP_COUNT;
    while (true) {
      int index;

      /*
       * Call gc() before we block in select().
       * It's work that has to be done anyway, and it's better
       * to avoid making every child do it.  It will also
       * madvise() any free memory as a side-effect.
       *
       * Don't call it every time, because walking the entire
       * heap is a lot of overhead to free a few hundred bytes.
       */
      if (loopCount <= 0) {
        gc();
        loopCount = GC_LOOP_COUNT;
      } else {
        loopCount--;
      }

      try {
        fdArray = fds.toArray(fdArray);
        index = selectReadable(fdArray);
      } catch (IOException ex) {
        throw new RuntimeException("Error in select()", ex);
      }

      if (index < 0) {
        throw new RuntimeException("Error in select()");
      } else if (index == 0) {
        ZygoteConnection newPeer = acceptCommandPeer();
        peers.add(newPeer);
        fds.add(newPeer.getFileDesciptor());
      } else {
        boolean done;
        done = peers.get(index).runOnce();

        if (done) {
          peers.remove(index);
          fds.remove(index);
        }
      }
    }
  }
  /** Prepare the arguments and fork for the system server process. */
  private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException {
    /* Hardcoded command line to start the system server */
    String args[] = {
      "--setuid=1000",
      "--setgid=1000",
      "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007",
      "--capabilities=130104352,130104352",
      "--runtime-init",
      "--nice-name=system_server",
      "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
      parsedArgs = new ZygoteConnection.Arguments(args);
      ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
      ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

      /* Request to fork the system server process */
      pid =
          Zygote.forkSystemServer(
              parsedArgs.uid,
              parsedArgs.gid,
              parsedArgs.gids,
              parsedArgs.debugFlags,
              null,
              parsedArgs.permittedCapabilities,
              parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
      throw new RuntimeException(ex);
    }

    /* For child process */
    if (pid == 0) {
      handleSystemServerProcess(parsedArgs);
    }

    return true;
  }
  /**
   * Runs the zygote process's select loop. Accepts new connections as they happen, and reads
   * commands from connections one spawn-request's worth at a time.
   *
   * @throws MethodAndArgsCaller in a child process when a main() should be executed.
   */
  private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);

    while (true) {
      StructPollfd[] pollFds = new StructPollfd[fds.size()];
      for (int i = 0; i < pollFds.length; ++i) {
        pollFds[i] = new StructPollfd();
        pollFds[i].fd = fds.get(i);
        pollFds[i].events = (short) POLLIN;
      }
      try {
        Os.poll(pollFds, -1);
      } catch (ErrnoException ex) {
        throw new RuntimeException("poll failed", ex);
      }
      for (int i = pollFds.length - 1; i >= 0; --i) {
        if ((pollFds[i].revents & POLLIN) == 0) {
          continue;
        }
        if (i == 0) {
          ZygoteConnection newPeer = acceptCommandPeer(abiList);
          peers.add(newPeer);
          fds.add(newPeer.getFileDesciptor());
        } else {
          boolean done = peers.get(i).runOnce();
          if (done) {
            peers.remove(i);
            fds.remove(i);
          }
        }
      }
    }
  }