/**
   * 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);
        }
      }
    }
  }
  /** Close and clean up zygote sockets. Called on shutdown and on the child's exit path. */
  static void closeServerSocket() {
    try {
      if (sServerSocket != null) {
        FileDescriptor fd = sServerSocket.getFileDescriptor();
        sServerSocket.close();
        if (fd != null) {
          Os.close(fd);
        }
      }
    } catch (IOException ex) {
      Log.e(TAG, "Zygote:  error closing sockets", ex);
    } catch (ErrnoException ex) {
      Log.e(TAG, "Zygote:  error closing descriptor", ex);
    }

    sServerSocket = null;
  }
  /**
   * 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);
          }
        }
      }
    }
  }
 /**
  * Return the server socket's underlying file descriptor, so that ZygoteConnection can pass it to
  * the native code for proper closure after a child process is forked off.
  */
 static FileDescriptor getServerSocketFileDescriptor() {
   return sServerSocket.getFileDescriptor();
 }