/**
   * Spawns and connects to a child process. May be called on any thread. It will not block, but
   * will instead callback to {@link #nativeOnChildProcessStarted} when the connection is
   * established. Note this callback will not necessarily be from the same thread (currently it
   * always comes from the main thread).
   *
   * @param context Context used to obtain the application context.
   * @param commandLine The child process command line argv.
   * @param fileIds The ID that should be used when mapping files in the created process.
   * @param fileFds The file descriptors that should be mapped in the created process.
   * @param fileAutoClose Whether the file descriptors should be closed once they were passed to the
   *     created process.
   * @param clientContext Arbitrary parameter used by the client to distinguish this connection.
   */
  @CalledByNative
  static void start(
      Context context,
      final String[] commandLine,
      int childProcessId,
      int[] fileIds,
      int[] fileFds,
      boolean[] fileAutoClose,
      long clientContext) {
    TraceEvent.begin();
    assert fileIds.length == fileFds.length && fileFds.length == fileAutoClose.length;
    FileDescriptorInfo[] filesToBeMapped = new FileDescriptorInfo[fileFds.length];
    for (int i = 0; i < fileFds.length; i++) {
      filesToBeMapped[i] = new FileDescriptorInfo(fileIds[i], fileFds[i], fileAutoClose[i]);
    }
    assert clientContext != 0;

    int callbackType = CALLBACK_FOR_UNKNOWN_PROCESS;
    boolean inSandbox = true;
    String processType = getSwitchValue(commandLine, SWITCH_PROCESS_TYPE);
    if (SWITCH_RENDERER_PROCESS.equals(processType)) {
      callbackType = CALLBACK_FOR_RENDERER_PROCESS;
    } else if (SWITCH_GPU_PROCESS.equals(processType)) {
      callbackType = CALLBACK_FOR_GPU_PROCESS;
    } else if (SWITCH_PPAPI_BROKER_PROCESS.equals(processType)) {
      inSandbox = false;
    }

    ChildProcessConnection allocatedConnection = null;
    synchronized (ChildProcessLauncher.class) {
      if (inSandbox) {
        allocatedConnection = sSpareSandboxedConnection;
        sSpareSandboxedConnection = null;
      }
    }
    if (allocatedConnection == null) {
      allocatedConnection = allocateBoundConnection(context, commandLine, inSandbox);
      if (allocatedConnection == null) {
        // Notify the native code so it can free the heap allocated callback.
        nativeOnChildProcessStarted(clientContext, 0);
        Log.e(TAG, "Allocation of new service failed.");
        TraceEvent.end();
        return;
      }
    }

    Log.d(TAG, "Setting up connection to process: slot=" + allocatedConnection.getServiceNumber());
    triggerConnectionSetup(
        allocatedConnection,
        commandLine,
        childProcessId,
        filesToBeMapped,
        callbackType,
        clientContext);
    TraceEvent.end();
  }
 private static ChildProcessConnection allocateBoundConnection(
     Context context, String[] commandLine, boolean inSandbox, boolean alwaysInForeground) {
   ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();
   ChildProcessConnection connection =
       allocateConnection(context, inSandbox, chromiumLinkerParams, alwaysInForeground);
   if (connection != null) {
     connection.start(commandLine);
   }
   return connection;
 }
 /**
  * Terminates a child process. This may be called from any thread.
  *
  * @param pid The pid (process handle) of the service connection obtained from {@link #start}.
  */
 @CalledByNative
 static void stop(int pid) {
   Log.d(TAG, "stopping child connection: pid=" + pid);
   ChildProcessConnection connection = sServiceMap.remove(pid);
   if (connection == null) {
     logPidWarning(pid, "Tried to stop non-existent connection");
     return;
   }
   sBindingManager.clearConnection(pid);
   connection.stop();
   freeConnection(connection);
 }
  private static void startInternal(
      Context context,
      final String[] commandLine,
      int childProcessId,
      FileDescriptorInfo[] filesToBeMapped,
      long clientContext,
      int callbackType,
      boolean inSandbox) {
    try {
      TraceEvent.begin("ChildProcessLauncher.startInternal");

      ChildProcessConnection allocatedConnection = null;
      synchronized (ChildProcessLauncher.class) {
        if (inSandbox) {
          allocatedConnection = sSpareSandboxedConnection;
          sSpareSandboxedConnection = null;
        }
      }
      if (allocatedConnection == null) {
        boolean alwaysInForeground = false;
        if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true;
        allocatedConnection =
            allocateBoundConnection(context, commandLine, inSandbox, alwaysInForeground);
        if (allocatedConnection == null) {
          Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn.");
          sPendingSpawnQueue.enqueue(
              new PendingSpawnData(
                  context,
                  commandLine,
                  childProcessId,
                  filesToBeMapped,
                  clientContext,
                  callbackType,
                  inSandbox));
          return;
        }
      }

      Log.d(
          TAG, "Setting up connection to process: slot=" + allocatedConnection.getServiceNumber());
      triggerConnectionSetup(
          allocatedConnection,
          commandLine,
          childProcessId,
          filesToBeMapped,
          callbackType,
          clientContext);
    } finally {
      TraceEvent.end("ChildProcessLauncher.startInternal");
    }
  }
  @VisibleForTesting
  static void triggerConnectionSetup(
      final ChildProcessConnection connection,
      String[] commandLine,
      int childProcessId,
      FileDescriptorInfo[] filesToBeMapped,
      final int callbackType,
      final long clientContext) {
    ChildProcessConnection.ConnectionCallback connectionCallback =
        new ChildProcessConnection.ConnectionCallback() {
          @Override
          public void onConnected(int pid) {
            Log.d(
                TAG,
                "on connect callback, pid="
                    + pid
                    + " context="
                    + clientContext
                    + " callbackType="
                    + callbackType);
            if (pid != NULL_PROCESS_HANDLE) {
              sBindingManager.addNewConnection(pid, connection);
              sServiceMap.put(pid, connection);
            }
            // If the connection fails and pid == 0, the Java-side cleanup was already
            // handled by DeathCallback. We still have to call back to native for
            // cleanup there.
            if (clientContext != 0) { // Will be 0 in Java instrumentation tests.
              nativeOnChildProcessStarted(clientContext, pid);
            }
          }
        };

    assert callbackType != CALLBACK_FOR_UNKNOWN_PROCESS;
    connection.setupConnection(
        commandLine,
        filesToBeMapped,
        createCallback(childProcessId, callbackType),
        connectionCallback,
        Linker.getSharedRelros());
  }
 public void free(ChildProcessConnection connection) {
   synchronized (mConnectionLock) {
     int slot = connection.getServiceNumber();
     if (mChildProcessConnections[slot] != connection) {
       int occupier =
           mChildProcessConnections[slot] == null
               ? -1
               : mChildProcessConnections[slot].getServiceNumber();
       Log.e(
           TAG,
           "Unable to find connection to free in slot: "
               + slot
               + " already occupied by service: "
               + occupier);
       assert false;
     } else {
       mChildProcessConnections[slot] = null;
       assert !mFreeConnectionIndices.contains(slot);
       mFreeConnectionIndices.add(slot);
     }
   }
 }
 private static void freeConnection(ChildProcessConnection connection) {
   getConnectionAllocator(connection.isInSandbox()).free(connection);
 }