This source file includes following definitions.
- JNINamespace
- setServiceClass
- allocate
- free
- setChildProcessClass
- getConnectionAllocator
- allocateConnection
- getLinkerParamsForNewConnection
- allocateBoundConnection
- freeConnection
- getBindingManager
- setBindingManagerForTesting
- isOomProtected
- onSentToBackground
- onBroughtToForeground
- getChildService
- warmUp
- getSwitchValue
- start
- stop
- createCallback
- logPidWarning
- nativeOnChildProcessStarted
- nativeGetViewSurface
- nativeGetSurfaceTextureSurface
- nativeEstablishSurfacePeer
- nativeIsSingleProcess
package org.chromium.content.browser;
import android.content.Context;
import android.util.Log;
import android.view.Surface;
import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.Linker;
import org.chromium.content.app.ChildProcessService;
import org.chromium.content.app.ChromiumLinkerParams;
import org.chromium.content.app.PrivilegedProcessService;
import org.chromium.content.app.SandboxedProcessService;
import org.chromium.content.common.IChildProcessCallback;
import org.chromium.content.common.IChildProcessService;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@JNINamespace("content")
public class ChildProcessLauncher {
private static final String TAG = "ChildProcessLauncher";
private static final int CALLBACK_FOR_UNKNOWN_PROCESS = 0;
private static final int CALLBACK_FOR_GPU_PROCESS = 1;
private static final int CALLBACK_FOR_RENDERER_PROCESS = 2;
private static final String SWITCH_PROCESS_TYPE = "type";
private static final String SWITCH_PPAPI_BROKER_PROCESS = "ppapi-broker";
private static final String SWITCH_RENDERER_PROCESS = "renderer";
private static final String SWITCH_GPU_PROCESS = "gpu-process";
static final int MAX_REGISTERED_SANDBOXED_SERVICES = 13;
static final int MAX_REGISTERED_PRIVILEGED_SERVICES = 3;
private static class ChildConnectionAllocator {
private final ChildProcessConnection[] mChildProcessConnections;
private final ArrayList<Integer> mFreeConnectionIndices;
private final Object mConnectionLock = new Object();
private Class<? extends ChildProcessService> mChildClass;
private final boolean mInSandbox;
public ChildConnectionAllocator(boolean inSandbox) {
int numChildServices = inSandbox ?
MAX_REGISTERED_SANDBOXED_SERVICES : MAX_REGISTERED_PRIVILEGED_SERVICES;
mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices];
mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);
for (int i = 0; i < numChildServices; i++) {
mFreeConnectionIndices.add(i);
}
setServiceClass(inSandbox ?
SandboxedProcessService.class : PrivilegedProcessService.class);
mInSandbox = inSandbox;
}
public void setServiceClass(Class<? extends ChildProcessService> childClass) {
mChildClass = childClass;
}
public ChildProcessConnection allocate(
Context context, ChildProcessConnection.DeathCallback deathCallback,
ChromiumLinkerParams chromiumLinkerParams) {
synchronized (mConnectionLock) {
if (mFreeConnectionIndices.isEmpty()) {
Log.w(TAG, "Ran out of service.");
return null;
}
int slot = mFreeConnectionIndices.remove(0);
assert mChildProcessConnections[slot] == null;
mChildProcessConnections[slot] = new ChildProcessConnectionImpl(context, slot,
mInSandbox, deathCallback, mChildClass, chromiumLinkerParams);
return mChildProcessConnections[slot];
}
}
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 final ChildConnectionAllocator sSandboxedChildConnectionAllocator =
new ChildConnectionAllocator(true);
private static final ChildConnectionAllocator sPrivilegedChildConnectionAllocator =
new ChildConnectionAllocator(false);
private static boolean sConnectionAllocated = false;
public static void setChildProcessClass(
Class<? extends SandboxedProcessService> sandboxedServiceClass,
Class<? extends PrivilegedProcessService> privilegedServiceClass) {
assert !sConnectionAllocated;
sSandboxedChildConnectionAllocator.setServiceClass(sandboxedServiceClass);
sPrivilegedChildConnectionAllocator.setServiceClass(privilegedServiceClass);
}
private static ChildConnectionAllocator getConnectionAllocator(boolean inSandbox) {
return inSandbox ?
sSandboxedChildConnectionAllocator : sPrivilegedChildConnectionAllocator;
}
private static ChildProcessConnection allocateConnection(Context context,
boolean inSandbox, ChromiumLinkerParams chromiumLinkerParams) {
ChildProcessConnection.DeathCallback deathCallback =
new ChildProcessConnection.DeathCallback() {
@Override
public void onChildProcessDied(int pid) {
stop(pid);
}
};
sConnectionAllocated = true;
return getConnectionAllocator(inSandbox).allocate(context, deathCallback,
chromiumLinkerParams);
}
private static boolean sLinkerInitialized = false;
private static long sLinkerLoadAddress = 0;
private static ChromiumLinkerParams getLinkerParamsForNewConnection() {
if (!sLinkerInitialized) {
if (Linker.isUsed()) {
sLinkerLoadAddress = Linker.getBaseLoadAddress();
if (sLinkerLoadAddress == 0) {
Log.i(TAG, "Shared RELRO support disabled!");
}
}
sLinkerInitialized = true;
}
if (sLinkerLoadAddress == 0)
return null;
final boolean waitForSharedRelros = true;
return new ChromiumLinkerParams(sLinkerLoadAddress,
waitForSharedRelros,
Linker.getTestRunnerClassName());
}
private static ChildProcessConnection allocateBoundConnection(Context context,
String[] commandLine, boolean inSandbox) {
ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();
ChildProcessConnection connection =
allocateConnection(context, inSandbox, chromiumLinkerParams);
if (connection != null) {
connection.start(commandLine);
}
return connection;
}
private static void freeConnection(ChildProcessConnection connection) {
if (connection == null) {
return;
}
getConnectionAllocator(connection.isInSandbox()).free(connection);
return;
}
private static final int NULL_PROCESS_HANDLE = 0;
private static Map<Integer, ChildProcessConnection> sServiceMap =
new ConcurrentHashMap<Integer, ChildProcessConnection>();
private static ChildProcessConnection sSpareSandboxedConnection = null;
private static BindingManager sBindingManager = BindingManagerImpl.createBindingManager();
static BindingManager getBindingManager() {
return sBindingManager;
}
@VisibleForTesting
public static void setBindingManagerForTesting(BindingManager manager) {
sBindingManager = manager;
}
@CalledByNative
private static boolean isOomProtected(int pid) {
return sBindingManager.isOomProtected(pid);
}
public static void onSentToBackground() {
sBindingManager.onSentToBackground();
}
public static void onBroughtToForeground() {
sBindingManager.onBroughtToForeground();
}
public static IChildProcessService getChildService(int pid) {
ChildProcessConnection connection = sServiceMap.get(pid);
if (connection != null) {
return connection.getService();
}
return null;
}
public static void warmUp(Context context) {
synchronized (ChildProcessLauncher.class) {
assert !ThreadUtils.runningOnUiThread();
if (sSpareSandboxedConnection == null) {
sSpareSandboxedConnection = allocateBoundConnection(context, null, true);
}
}
}
private static String getSwitchValue(final String[] commandLine, String switchKey) {
if (commandLine == null || switchKey == null) {
return null;
}
final String switchKeyPrefix = "--" + switchKey + "=";
for (String command : commandLine) {
if (command != null && command.startsWith(switchKeyPrefix)) {
return command.substring(switchKeyPrefix.length());
}
}
return null;
}
@CalledByNative
static void start(
Context context,
final String[] commandLine,
int childProcessId,
int[] fileIds,
int[] fileFds,
boolean[] fileAutoClose,
final long clientContext) {
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) {
nativeOnChildProcessStarted(clientContext, 0);
return;
}
}
final ChildProcessConnection connection = allocatedConnection;
Log.d(TAG, "Setting up connection to process: slot=" + connection.getServiceNumber());
ChildProcessConnection.ConnectionCallback connectionCallback =
new ChildProcessConnection.ConnectionCallback() {
@Override
public void onConnected(int pid) {
Log.d(TAG, "on connect callback, pid=" + pid + " context=" + clientContext);
if (pid != NULL_PROCESS_HANDLE) {
sBindingManager.addNewConnection(pid, connection);
sServiceMap.put(pid, connection);
} else {
freeConnection(connection);
}
nativeOnChildProcessStarted(clientContext, pid);
}
};
connection.setupConnection(commandLine,
filesToBeMapped,
createCallback(childProcessId, callbackType),
connectionCallback,
Linker.getSharedRelros());
}
@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 IChildProcessCallback createCallback(
final int childProcessId, final int callbackType) {
return new IChildProcessCallback.Stub() {
@Override
public void establishSurfacePeer(
int pid, Surface surface, int primaryID, int secondaryID) {
if (callbackType != CALLBACK_FOR_GPU_PROCESS) {
Log.e(TAG, "Illegal callback for non-GPU process.");
return;
}
nativeEstablishSurfacePeer(pid, surface, primaryID, secondaryID);
}
@Override
public Surface getViewSurface(int surfaceId) {
if (callbackType != CALLBACK_FOR_GPU_PROCESS) {
Log.e(TAG, "Illegal callback for non-GPU process.");
return null;
}
return nativeGetViewSurface(surfaceId);
}
@Override
public Surface getSurfaceTextureSurface(int primaryId, int secondaryId) {
if (callbackType != CALLBACK_FOR_RENDERER_PROCESS) {
Log.e(TAG, "Illegal callback for non-renderer process.");
return null;
}
if (secondaryId != childProcessId) {
Log.e(TAG, "Illegal secondaryId for renderer process.");
return null;
}
return nativeGetSurfaceTextureSurface(primaryId, secondaryId);
}
};
}
static void logPidWarning(int pid, String message) {
if (pid > 0 && !nativeIsSingleProcess()) {
Log.w(TAG, message + ", pid=" + pid);
}
}
private static native void nativeOnChildProcessStarted(long clientContext, int pid);
private static native Surface nativeGetViewSurface(int surfaceId);
private static native Surface nativeGetSurfaceTextureSurface(
int surfaceTextureId, int childProcessId);
private static native void nativeEstablishSurfacePeer(
int pid, Surface surface, int primaryID, int secondaryID);
private static native boolean nativeIsSingleProcess();
}