Chromium Code Reviews| 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..388a4d17ec9d399a3387e870978da89fee27b1df 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 |
| @@ -26,6 +26,7 @@ import org.chromium.base.TraceEvent; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.base.annotations.CalledByNative; |
| import org.chromium.base.annotations.JNINamespace; |
| +import org.chromium.base.library_loader.LibraryProcessType; |
| import org.chromium.base.library_loader.Linker; |
| import org.chromium.content.app.ChildProcessService; |
| import org.chromium.content.app.ChromiumLinkerParams; |
| @@ -38,6 +39,7 @@ import org.chromium.content.common.SurfaceWrapper; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| +import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.Map; |
| import java.util.Queue; |
| @@ -67,6 +69,9 @@ public class ChildProcessLauncher { |
| private Class<? extends ChildProcessService> mChildClass; |
| private final boolean mInSandbox; |
| + // Each Allocator keeps an 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 PendingSpawnQueue mPendingSpawnQueue = new PendingSpawnQueue(); |
| public ChildConnectionAllocator(boolean inSandbox, int numChildServices) { |
| mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices]; |
| @@ -125,6 +130,10 @@ public class ChildProcessLauncher { |
| } |
| } |
| + public PendingSpawnQueue getPendingSpawnQueue() { |
| + return mPendingSpawnQueue; |
| + } |
| + |
| /** @return the count of connections managed by the allocator */ |
| @VisibleForTesting |
| int allocatedConnectionsCountForTesting() { |
| @@ -140,6 +149,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 +158,8 @@ public class ChildProcessLauncher { |
| FileDescriptorInfo[] filesToBeMapped, |
| long clientContext, |
| int callbackType, |
| - boolean inSandbox) { |
| + boolean inSandbox, |
| + ChildProcessCreationParams creationParams) { |
| mContext = context; |
| mCommandLine = commandLine; |
| mChildProcessId = childProcessId; |
| @@ -156,6 +167,7 @@ public class ChildProcessLauncher { |
| mClientContext = clientContext; |
| mCallbackType = callbackType; |
| mInSandbox = inSandbox; |
| + mCreationParams = creationParams; |
| } |
| private Context context() { |
| @@ -179,22 +191,25 @@ 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 = |
| + private Queue<PendingSpawnData> mPendingSpawns = |
| new LinkedList<PendingSpawnData>(); |
| - static final Object sPendingSpawnsLock = new Object(); |
| + 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 +217,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 +242,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 +275,55 @@ public class ChildProcessLauncher { |
| } |
| } |
| - private static void initConnectionAllocatorsIfNecessary(Context context) { |
| + private static void initConnectionAllocatorsIfNecessary( |
| + Context context, boolean inSandbox, String packageName) { |
| 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 HashMap<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, |
| + inSandbox, packageName))); |
|
pkotwicz
2016/05/31 00:38:58
Nit: inSandbox -> true
|
| + } |
| + } 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}. |
| + } |
| + } |
| + |
| + /** |
| + * 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); |
| } |
| - private static ChildConnectionAllocator getConnectionAllocator(boolean inSandbox) { |
| - return inSandbox |
| - ? sSandboxedChildConnectionAllocator : sPrivilegedChildConnectionAllocator; |
| + /** |
| + * 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 +335,11 @@ public class ChildProcessLauncher { |
| } |
| } |
| }; |
| - initConnectionAllocatorsIfNecessary(context); |
| - return getConnectionAllocator(inSandbox).allocate(context, deathCallback, |
| - chromiumLinkerParams, alwaysInForeground, ChildProcessCreationParams.get()); |
| + String packageName = creationParams.getPackageName(); |
| + initConnectionAllocatorsIfNecessary(context, inSandbox, packageName); |
| + return getConnectionAllocator(packageName, inSandbox) |
| + .allocate(context, deathCallback, chromiumLinkerParams, alwaysInForeground, |
| + creationParams); |
| } |
| private static boolean sLinkerInitialized = false; |
| @@ -329,14 +373,17 @@ 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()) { |
| + if (inSandbox |
| + && !getConnectionAllocator(creationParams.getPackageName(), 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.getCreationParams().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,13 @@ 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); |
| + sSpareSandboxedConnection = allocateBoundConnection(context, null, true, false, |
| + ChildProcessCreationParams.get().copy()); |
| } |
| } |
| } |
| @@ -594,11 +648,15 @@ public class ChildProcessLauncher { |
| boolean inSandbox = true; |
| String processType = |
| ContentSwitches.getSwitchValue(commandLine, ContentSwitches.SWITCH_PROCESS_TYPE); |
| + ChildProcessCreationParams params = ChildProcessCreationParams.get().copy(); |
| if (ContentSwitches.SWITCH_RENDERER_PROCESS.equals(processType)) { |
| callbackType = CALLBACK_FOR_RENDERER_PROCESS; |
| } else if (ContentSwitches.SWITCH_GPU_PROCESS.equals(processType)) { |
| callbackType = CALLBACK_FOR_GPU_PROCESS; |
| inSandbox = false; |
| + // For GPU process, always set the Chrome's package name. |
| + params = new ChildProcessCreationParams( |
| + context.getPackageName(), 0, LibraryProcessType.PROCESS_CHILD); |
| } else if (ContentSwitches.SWITCH_UTILITY_PROCESS.equals(processType)) { |
| // We only support sandboxed right now. |
| callbackType = CALLBACK_FOR_UTILITY_PROCESS; |
| @@ -607,7 +665,7 @@ public class ChildProcessLauncher { |
| } |
| startInternal(context, commandLine, childProcessId, filesToBeMapped, clientContext, |
| - callbackType, inSandbox); |
| + callbackType, inSandbox, params); |
| } |
| /** |
| @@ -650,13 +708,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.getPackageName(); |
| synchronized (ChildProcessLauncher.class) { |
| - if (inSandbox) { |
| + if (inSandbox && sSpareSandboxedConnection != null |
| + && sSpareSandboxedConnection.getCreationParams().getPackageName().equals( |
| + packageName)) { |
| allocatedConnection = sSpareSandboxedConnection; |
| sSpareSandboxedConnection = null; |
| } |
| @@ -664,14 +726,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 +912,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 +949,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(); |
| } |
| } |