| Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| index caf965ae8c36f030844bc39ee94d2f180067bc6a..daa8aef6caa8d07814f24bbe61e1f2349597d96e 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| @@ -67,6 +67,9 @@ public class ChildProcessLauncher {
|
|
|
| private Class<? extends ChildProcessService> mChildClass;
|
| private final boolean mInSandbox;
|
| + // Each Allocator keeps a queue for the pending spawn data. Once a connection is free, we
|
| + // dequeue the pending spawn data from the same allocator as the connection.
|
| + private final PendingSpawnQueue mPendingSpawnQueue = new PendingSpawnQueue();
|
|
|
| public ChildConnectionAllocator(boolean inSandbox, int numChildServices) {
|
| mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices];
|
| @@ -125,6 +128,10 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
|
|
| + public PendingSpawnQueue getPendingSpawnQueue() {
|
| + return mPendingSpawnQueue;
|
| + }
|
| +
|
| /** @return the count of connections managed by the allocator */
|
| @VisibleForTesting
|
| int allocatedConnectionsCountForTesting() {
|
| @@ -140,6 +147,7 @@ public class ChildProcessLauncher {
|
| private final long mClientContext;
|
| private final int mCallbackType;
|
| private final boolean mInSandbox;
|
| + private final ChildProcessCreationParams mCreationParams;
|
|
|
| private PendingSpawnData(
|
| Context context,
|
| @@ -148,7 +156,8 @@ public class ChildProcessLauncher {
|
| FileDescriptorInfo[] filesToBeMapped,
|
| long clientContext,
|
| int callbackType,
|
| - boolean inSandbox) {
|
| + boolean inSandbox,
|
| + ChildProcessCreationParams creationParams) {
|
| mContext = context;
|
| mCommandLine = commandLine;
|
| mChildProcessId = childProcessId;
|
| @@ -156,6 +165,7 @@ public class ChildProcessLauncher {
|
| mClientContext = clientContext;
|
| mCallbackType = callbackType;
|
| mInSandbox = inSandbox;
|
| + mCreationParams = creationParams;
|
| }
|
|
|
| private Context context() {
|
| @@ -179,22 +189,24 @@ public class ChildProcessLauncher {
|
| private boolean inSandbox() {
|
| return mInSandbox;
|
| }
|
| + private ChildProcessCreationParams getCreationParams() {
|
| + return mCreationParams;
|
| + }
|
| }
|
|
|
| private static class PendingSpawnQueue {
|
| // The list of pending process spawn requests and its lock.
|
| // Operations on this queue must be atomical w.r.t. free connections updates.
|
| - private static Queue<PendingSpawnData> sPendingSpawns =
|
| - new LinkedList<PendingSpawnData>();
|
| - static final Object sPendingSpawnsLock = new Object();
|
| + private Queue<PendingSpawnData> mPendingSpawns = new LinkedList<PendingSpawnData>();
|
| + final Object mPendingSpawnsLock = new Object();
|
|
|
| /**
|
| * Queue up a spawn requests to be processed once a free service is available.
|
| * Called when a spawn is requested while we are at the capacity.
|
| */
|
| public void enqueueLocked(final PendingSpawnData pendingSpawn) {
|
| - assert Thread.holdsLock(sPendingSpawnsLock);
|
| - sPendingSpawns.add(pendingSpawn);
|
| + assert Thread.holdsLock(mPendingSpawnsLock);
|
| + mPendingSpawns.add(pendingSpawn);
|
| }
|
|
|
| /**
|
| @@ -202,22 +214,21 @@ public class ChildProcessLauncher {
|
| * @return the next spawn request waiting in the queue.
|
| */
|
| public PendingSpawnData dequeueLocked() {
|
| - assert Thread.holdsLock(sPendingSpawnsLock);
|
| - return sPendingSpawns.poll();
|
| + assert Thread.holdsLock(mPendingSpawnsLock);
|
| + return mPendingSpawns.poll();
|
| }
|
|
|
| /** @return the count of pending spawns in the queue */
|
| public int sizeLocked() {
|
| - assert Thread.holdsLock(sPendingSpawnsLock);
|
| - return sPendingSpawns.size();
|
| + assert Thread.holdsLock(mPendingSpawnsLock);
|
| + return mPendingSpawns.size();
|
| }
|
| }
|
|
|
| - private static final PendingSpawnQueue sPendingSpawnQueue = new PendingSpawnQueue();
|
| -
|
| - // Service class for child process. As the default value it uses SandboxedProcessService0 and
|
| - // PrivilegedProcessService0.
|
| - private static ChildConnectionAllocator sSandboxedChildConnectionAllocator;
|
| + // Service class for child process.
|
| + // Map from package name to ChildConnectionAllocator.
|
| + private static Map<String, ChildConnectionAllocator> sSandboxedChildConnectionAllocatorMap;
|
| + // As the default value it uses PrivilegedProcessService0.
|
| private static ChildConnectionAllocator sPrivilegedChildConnectionAllocator;
|
|
|
| private static final String NUM_SANDBOXED_SERVICES_KEY =
|
| @@ -228,17 +239,16 @@ public class ChildProcessLauncher {
|
| @VisibleForTesting
|
| public static final String SWITCH_NUM_SANDBOXED_SERVICES_FOR_TESTING = "num-sandboxed-services";
|
|
|
| - private static int getNumberOfServices(Context context, boolean inSandbox) {
|
| + private static int getNumberOfServices(Context context, boolean inSandbox, String packageName) {
|
| try {
|
| PackageManager packageManager = context.getPackageManager();
|
| - ChildProcessCreationParams childProcessCreationParams =
|
| - ChildProcessCreationParams.get();
|
| - final String packageName = childProcessCreationParams != null
|
| - ? childProcessCreationParams.getPackageName() : context.getPackageName();
|
| ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName,
|
| PackageManager.GET_META_DATA);
|
| - int numServices = appInfo.metaData.getInt(inSandbox ? NUM_SANDBOXED_SERVICES_KEY
|
| - : NUM_PRIVILEGED_SERVICES_KEY, -1);
|
| + int numServices = -1;
|
| + if (appInfo.metaData != null) {
|
| + numServices = appInfo.metaData.getInt(
|
| + inSandbox ? NUM_SANDBOXED_SERVICES_KEY : NUM_PRIVILEGED_SERVICES_KEY, -1);
|
| + }
|
| if (inSandbox
|
| && CommandLine.getInstance().hasSwitch(
|
| SWITCH_NUM_SANDBOXED_SERVICES_FOR_TESTING)) {
|
| @@ -262,26 +272,56 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
|
|
| - private static void initConnectionAllocatorsIfNecessary(Context context) {
|
| + private static void initConnectionAllocatorsIfNecessary(
|
| + Context context, boolean inSandbox, String packageName) {
|
| + // TODO(mariakhomenko): Uses an Object to lock the access.
|
| synchronized (ChildProcessLauncher.class) {
|
| - if (sSandboxedChildConnectionAllocator == null) {
|
| - sSandboxedChildConnectionAllocator =
|
| - new ChildConnectionAllocator(true, getNumberOfServices(context, true));
|
| - }
|
| - if (sPrivilegedChildConnectionAllocator == null) {
|
| - sPrivilegedChildConnectionAllocator =
|
| - new ChildConnectionAllocator(false, getNumberOfServices(context, false));
|
| + if (inSandbox) {
|
| + if (sSandboxedChildConnectionAllocatorMap == null) {
|
| + sSandboxedChildConnectionAllocatorMap =
|
| + new ConcurrentHashMap<String, ChildConnectionAllocator>();
|
| + }
|
| + if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageName)) {
|
| + Log.w(TAG, "Create a new ChildConnectionAllocator with package name = %s,"
|
| + + " inSandbox = true",
|
| + packageName);
|
| + sSandboxedChildConnectionAllocatorMap.put(packageName,
|
| + new ChildConnectionAllocator(true,
|
| + getNumberOfServices(context, true, packageName)));
|
| + }
|
| + } else if (sPrivilegedChildConnectionAllocator == null) {
|
| + sPrivilegedChildConnectionAllocator = new ChildConnectionAllocator(
|
| + false, getNumberOfServices(context, false, packageName));
|
| }
|
| + // TODO(pkotwicz|hanxi): Figure out when old allocators should be removed from
|
| + // {@code sSandboxedChildConnectionAllocatorMap}.
|
| }
|
| }
|
|
|
| - private static ChildConnectionAllocator getConnectionAllocator(boolean inSandbox) {
|
| - return inSandbox
|
| - ? sSandboxedChildConnectionAllocator : sPrivilegedChildConnectionAllocator;
|
| + /**
|
| + * Note: please make sure that the Allocator has been initialized before calling this function.
|
| + * Otherwise, always calls {@link initConnectionAllocatorsIfNecessary} first.
|
| + */
|
| + private static ChildConnectionAllocator getConnectionAllocator(
|
| + String packageName, boolean inSandbox) {
|
| + if (!inSandbox) {
|
| + return sPrivilegedChildConnectionAllocator;
|
| + }
|
| + return sSandboxedChildConnectionAllocatorMap.get(packageName);
|
| + }
|
| +
|
| + /**
|
| + * Get the PendingSpawnQueue of the Allocator. Initialize the Allocator if needed.
|
| + */
|
| + private static PendingSpawnQueue getPendingSpawnQueue(Context context, String packageName,
|
| + boolean inSandbox) {
|
| + initConnectionAllocatorsIfNecessary(context, inSandbox, packageName);
|
| + return getConnectionAllocator(packageName, inSandbox).getPendingSpawnQueue();
|
| }
|
|
|
| private static ChildProcessConnection allocateConnection(Context context, boolean inSandbox,
|
| - ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForeground) {
|
| + ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForeground,
|
| + ChildProcessCreationParams creationParams) {
|
| ChildProcessConnection.DeathCallback deathCallback =
|
| new ChildProcessConnection.DeathCallback() {
|
| @Override
|
| @@ -293,9 +333,12 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
| };
|
| - initConnectionAllocatorsIfNecessary(context);
|
| - return getConnectionAllocator(inSandbox).allocate(context, deathCallback,
|
| - chromiumLinkerParams, alwaysInForeground, ChildProcessCreationParams.get());
|
| + String packageName = creationParams != null ? creationParams.getPackageName()
|
| + : context.getPackageName();
|
| + initConnectionAllocatorsIfNecessary(context, inSandbox, packageName);
|
| + return getConnectionAllocator(packageName, inSandbox)
|
| + .allocate(context, deathCallback, chromiumLinkerParams, alwaysInForeground,
|
| + creationParams);
|
| }
|
|
|
| private static boolean sLinkerInitialized = false;
|
| @@ -329,14 +372,18 @@ public class ChildProcessLauncher {
|
| }
|
|
|
| private static ChildProcessConnection allocateBoundConnection(Context context,
|
| - String[] commandLine, boolean inSandbox, boolean alwaysInForeground) {
|
| + String[] commandLine, boolean inSandbox, boolean alwaysInForeground,
|
| + ChildProcessCreationParams creationParams) {
|
| ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();
|
| - ChildProcessConnection connection = allocateConnection(context, inSandbox,
|
| - chromiumLinkerParams, alwaysInForeground);
|
| + ChildProcessConnection connection = allocateConnection(
|
| + context, inSandbox, chromiumLinkerParams, alwaysInForeground, creationParams);
|
| if (connection != null) {
|
| connection.start(commandLine);
|
|
|
| - if (inSandbox && !sSandboxedChildConnectionAllocator.isFreeConnectionAvailable()) {
|
| + String packageName = creationParams != null ? creationParams.getPackageName()
|
| + : context.getPackageName();
|
| + if (inSandbox && !getConnectionAllocator(packageName, inSandbox)
|
| + .isFreeConnectionAvailable()) {
|
| // Proactively releases all the moderate bindings once all the sandboxed services
|
| // are allocated, which will be very likely to have some of them killed by OOM
|
| // killer.
|
| @@ -370,7 +417,7 @@ public class ChildProcessLauncher {
|
| startInternal(pendingSpawn.context(), pendingSpawn.commandLine(),
|
| pendingSpawn.childProcessId(), pendingSpawn.filesToBeMapped(),
|
| pendingSpawn.clientContext(), pendingSpawn.callbackType(),
|
| - pendingSpawn.inSandbox());
|
| + pendingSpawn.inSandbox(), pendingSpawn.getCreationParams());
|
| }
|
| }).start();
|
| }
|
| @@ -379,9 +426,13 @@ public class ChildProcessLauncher {
|
| }
|
|
|
| private static PendingSpawnData freeConnectionAndDequeuePending(ChildProcessConnection conn) {
|
| - synchronized (PendingSpawnQueue.sPendingSpawnsLock) {
|
| - getConnectionAllocator(conn.isInSandbox()).free(conn);
|
| - return sPendingSpawnQueue.dequeueLocked();
|
| + ChildConnectionAllocator allocator = getConnectionAllocator(
|
| + conn.getPackageName(), conn.isInSandbox());
|
| + assert allocator != null;
|
| + PendingSpawnQueue pendingSpawnQueue = allocator.getPendingSpawnQueue();
|
| + synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| + allocator.free(conn);
|
| + return pendingSpawnQueue.dequeueLocked();
|
| }
|
| }
|
|
|
| @@ -513,6 +564,9 @@ public class ChildProcessLauncher {
|
|
|
| /**
|
| * Starts moderate binding management.
|
| + * Note: WebAPKs and non WebAPKs share the same moderate binding pool, so the size of the
|
| + * shared moderate binding pool is always set based on the number of sandboxes processes
|
| + * used by Chrome.
|
| * @param context Android's context.
|
| * @param moderateBindingTillBackgrounded true if the BindingManager should add a moderate
|
| * binding to a render process when it is created and remove the moderate binding when Chrome is
|
| @@ -520,8 +574,9 @@ public class ChildProcessLauncher {
|
| */
|
| public static void startModerateBindingManagement(
|
| Context context, boolean moderateBindingTillBackgrounded) {
|
| - sBindingManager.startModerateBindingManagement(
|
| - context, getNumberOfServices(context, true), moderateBindingTillBackgrounded);
|
| + sBindingManager.startModerateBindingManagement(context,
|
| + getNumberOfServices(context, true, context.getPackageName()),
|
| + moderateBindingTillBackgrounded);
|
| }
|
|
|
| /**
|
| @@ -544,14 +599,17 @@ public class ChildProcessLauncher {
|
| * in parallel to other startup work. Must not be called on the UI thread. Spare connection is
|
| * created in sandboxed child process.
|
| * @param context the application context used for the connection.
|
| - * @param params child process creation params.
|
| */
|
| - public static void warmUp(Context context, ChildProcessCreationParams params) {
|
| - ChildProcessCreationParams.set(params);
|
| + public static void warmUp(Context context) {
|
| synchronized (ChildProcessLauncher.class) {
|
| assert !ThreadUtils.runningOnUiThread();
|
| if (sSpareSandboxedConnection == null) {
|
| - sSpareSandboxedConnection = allocateBoundConnection(context, null, true, false);
|
| + ChildProcessCreationParams params = ChildProcessCreationParams.get();
|
| + if (params != null) {
|
| + params = params.copy();
|
| + }
|
| + sSpareSandboxedConnection = allocateBoundConnection(context, null, true, false,
|
| + params);
|
| }
|
| }
|
| }
|
| @@ -594,8 +652,12 @@ public class ChildProcessLauncher {
|
| boolean inSandbox = true;
|
| String processType =
|
| ContentSwitches.getSwitchValue(commandLine, ContentSwitches.SWITCH_PROCESS_TYPE);
|
| + ChildProcessCreationParams params = null;
|
| if (ContentSwitches.SWITCH_RENDERER_PROCESS.equals(processType)) {
|
| callbackType = CALLBACK_FOR_RENDERER_PROCESS;
|
| + if (ChildProcessCreationParams.get() != null) {
|
| + params = ChildProcessCreationParams.get().copy();
|
| + }
|
| } else if (ContentSwitches.SWITCH_GPU_PROCESS.equals(processType)) {
|
| callbackType = CALLBACK_FOR_GPU_PROCESS;
|
| inSandbox = false;
|
| @@ -607,7 +669,7 @@ public class ChildProcessLauncher {
|
| }
|
|
|
| startInternal(context, commandLine, childProcessId, filesToBeMapped, clientContext,
|
| - callbackType, inSandbox);
|
| + callbackType, inSandbox, params);
|
| }
|
|
|
| /**
|
| @@ -650,13 +712,17 @@ public class ChildProcessLauncher {
|
| FileDescriptorInfo[] filesToBeMapped,
|
| long clientContext,
|
| int callbackType,
|
| - boolean inSandbox) {
|
| + boolean inSandbox,
|
| + ChildProcessCreationParams creationParams) {
|
| try {
|
| TraceEvent.begin("ChildProcessLauncher.startInternal");
|
|
|
| ChildProcessConnection allocatedConnection = null;
|
| + String packageName = creationParams != null ? creationParams.getPackageName()
|
| + : context.getPackageName();
|
| synchronized (ChildProcessLauncher.class) {
|
| - if (inSandbox) {
|
| + if (inSandbox && sSpareSandboxedConnection != null
|
| + && sSpareSandboxedConnection.getPackageName().equals(packageName)) {
|
| allocatedConnection = sSpareSandboxedConnection;
|
| sSpareSandboxedConnection = null;
|
| }
|
| @@ -664,14 +730,16 @@ public class ChildProcessLauncher {
|
| if (allocatedConnection == null) {
|
| boolean alwaysInForeground = false;
|
| if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true;
|
| - synchronized (PendingSpawnQueue.sPendingSpawnsLock) {
|
| + PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(
|
| + context, packageName, inSandbox);
|
| + synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| allocatedConnection = allocateBoundConnection(
|
| - context, commandLine, inSandbox, alwaysInForeground);
|
| + context, commandLine, inSandbox, alwaysInForeground, creationParams);
|
| if (allocatedConnection == null) {
|
| Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn.");
|
| - sPendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine,
|
| + pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine,
|
| childProcessId, filesToBeMapped, clientContext,
|
| - callbackType, inSandbox));
|
| + callbackType, inSandbox, creationParams));
|
| return;
|
| }
|
| }
|
| @@ -848,26 +916,35 @@ public class ChildProcessLauncher {
|
| }
|
|
|
| @VisibleForTesting
|
| - static ChildProcessConnection allocateBoundConnectionForTesting(Context context) {
|
| - return allocateBoundConnection(context, null, true, false);
|
| + static ChildProcessConnection allocateBoundConnectionForTesting(Context context,
|
| + ChildProcessCreationParams creationParams) {
|
| + return allocateBoundConnection(context, null, true, false, creationParams);
|
| }
|
|
|
| /**
|
| * Queue up a spawn requests for testing.
|
| */
|
| @VisibleForTesting
|
| - static void enqueuePendingSpawnForTesting(Context context, String[] commandLine) {
|
| - synchronized (PendingSpawnQueue.sPendingSpawnsLock) {
|
| - sPendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine, 1,
|
| - new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true));
|
| + static void enqueuePendingSpawnForTesting(Context context, String[] commandLine,
|
| + ChildProcessCreationParams creationParams, boolean inSandbox) {
|
| + PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context,
|
| + creationParams.getPackageName(), inSandbox);
|
| + synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| + pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine, 1,
|
| + new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true,
|
| + creationParams));
|
| }
|
| }
|
|
|
| - /** @return the count of sandboxed connections managed by the allocator */
|
| + /**
|
| + * @return the number of sandboxed connections of given {@link packageName} managed by the
|
| + * allocator.
|
| + */
|
| @VisibleForTesting
|
| - static int allocatedConnectionsCountForTesting(Context context) {
|
| - initConnectionAllocatorsIfNecessary(context);
|
| - return sSandboxedChildConnectionAllocator.allocatedConnectionsCountForTesting();
|
| + static int allocatedSandboxedConnectionsCountForTesting(Context context, String packageName) {
|
| + initConnectionAllocatorsIfNecessary(context, true, packageName);
|
| + return sSandboxedChildConnectionAllocatorMap.get(packageName)
|
| + .allocatedConnectionsCountForTesting();
|
| }
|
|
|
| /** @return the count of services set up and working */
|
| @@ -876,11 +953,18 @@ public class ChildProcessLauncher {
|
| return sServiceMap.size();
|
| }
|
|
|
| - /** @return the count of pending spawns in the queue */
|
| + /**
|
| + * @param context The context.
|
| + * @param packageName The package name of the {@link ChildProcessAlocator}.
|
| + * @param inSandbox Whether the connection is sandboxed.
|
| + * @return the count of pending spawns in the queue.
|
| + */
|
| @VisibleForTesting
|
| - static int pendingSpawnsCountForTesting() {
|
| - synchronized (PendingSpawnQueue.sPendingSpawnsLock) {
|
| - return sPendingSpawnQueue.sizeLocked();
|
| + static int pendingSpawnsCountForTesting(Context context, String packageName,
|
| + boolean inSandbox) {
|
| + PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, packageName, inSandbox);
|
| + synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| + return pendingSpawnQueue.sizeLocked();
|
| }
|
| }
|
|
|
|
|