/** * 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); }