| 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 aed55c3f5328b61e80ea44f357b26f3418a24258..3ae8def576b792254f5e7a6df9383ab1dbdbbd96 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
|
| @@ -8,6 +8,7 @@ import android.content.ComponentName;
|
| import android.content.Context;
|
| import android.content.pm.ApplicationInfo;
|
| import android.content.pm.PackageManager;
|
| +import android.os.AsyncTask;
|
| import android.os.Bundle;
|
| import android.os.ParcelFileDescriptor;
|
| import android.os.RemoteException;
|
| @@ -413,12 +414,13 @@ public class ChildProcessLauncher {
|
|
|
| private static ChildProcessConnection allocateBoundConnection(Context context,
|
| String[] commandLine, boolean inSandbox, boolean alwaysInForeground,
|
| - ChildProcessCreationParams creationParams) {
|
| + ChildProcessCreationParams creationParams,
|
| + ChildProcessConnection.StartCallback startCallback) {
|
| ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();
|
| ChildProcessConnection connection = allocateConnection(
|
| context, inSandbox, chromiumLinkerParams, alwaysInForeground, creationParams);
|
| if (connection != null) {
|
| - connection.start(commandLine);
|
| + connection.start(commandLine, startCallback);
|
|
|
| String packageName = creationParams != null ? creationParams.getPackageName()
|
| : context.getPackageName();
|
| @@ -436,7 +438,7 @@ public class ChildProcessLauncher {
|
| private static final long FREE_CONNECTION_DELAY_MILLIS = 1;
|
|
|
| private static void freeConnection(ChildProcessConnection connection) {
|
| - synchronized (ChildProcessLauncher.class) {
|
| + synchronized (sSpareConnectionLock) {
|
| if (connection.equals(sSpareSandboxedConnection)) sSpareSandboxedConnection = null;
|
| }
|
|
|
| @@ -483,8 +485,16 @@ public class ChildProcessLauncher {
|
| private static Map<Integer, ChildProcessConnection> sServiceMap =
|
| new ConcurrentHashMap<Integer, ChildProcessConnection>();
|
|
|
| + // Lock and monitor for these members {{{
|
| + private static final Object sSpareConnectionLock = new Object();
|
| // A pre-allocated and pre-bound connection ready for connection setup, or null.
|
| private static ChildProcessConnection sSpareSandboxedConnection;
|
| + // If sSpareSandboxedConnection is not null, this indicates whether the service is
|
| + // ready for connection setup. Wait on the monitor lock to be notified when this
|
| + // state changes. sSpareSandboxedConnection may be null after waiting, if starting
|
| + // the service failed.
|
| + private static boolean sSpareConnectionStarting;
|
| + // }}}
|
|
|
| // Manages oom bindings used to bind chind services.
|
| private static BindingManager sBindingManager = BindingManagerImpl.createBindingManager();
|
| @@ -567,15 +577,38 @@ public class ChildProcessLauncher {
|
| * @param context the application context used for the connection.
|
| */
|
| public static void warmUp(Context context) {
|
| - synchronized (ChildProcessLauncher.class) {
|
| + synchronized (sSpareConnectionLock) {
|
| assert !ThreadUtils.runningOnUiThread();
|
| if (sSpareSandboxedConnection == null) {
|
| ChildProcessCreationParams params = ChildProcessCreationParams.get();
|
| if (params != null) {
|
| params = params.copy();
|
| }
|
| +
|
| + sSpareConnectionStarting = true;
|
| +
|
| + ChildProcessConnection.StartCallback startCallback =
|
| + new ChildProcessConnection.StartCallback() {
|
| + @Override
|
| + public void onChildStarted() {
|
| + synchronized (sSpareConnectionLock) {
|
| + sSpareConnectionStarting = false;
|
| + sSpareConnectionLock.notify();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onChildStartFailed() {
|
| + Log.e(TAG, "Failed to warm up the spare sandbox service");
|
| + synchronized (sSpareConnectionLock) {
|
| + sSpareSandboxedConnection = null;
|
| + sSpareConnectionStarting = false;
|
| + sSpareConnectionLock.notify();
|
| + }
|
| + }
|
| + };
|
| sSpareSandboxedConnection = allocateBoundConnection(context, null, true, false,
|
| - params);
|
| + params, startCallback);
|
| }
|
| }
|
| }
|
| @@ -653,24 +686,30 @@ public class ChildProcessLauncher {
|
| callbackType, inSandbox, params);
|
| }
|
|
|
| - private static void startInternal(
|
| - Context context,
|
| + private static ChildProcessConnection startInternal(
|
| + final Context context,
|
| final String[] commandLine,
|
| - int childProcessId,
|
| - FileDescriptorInfo[] filesToBeMapped,
|
| - long clientContext,
|
| - int callbackType,
|
| - boolean inSandbox,
|
| - ChildProcessCreationParams creationParams) {
|
| + final int childProcessId,
|
| + final FileDescriptorInfo[] filesToBeMapped,
|
| + final long clientContext,
|
| + final int callbackType,
|
| + final boolean inSandbox,
|
| + final ChildProcessCreationParams creationParams) {
|
| try {
|
| TraceEvent.begin("ChildProcessLauncher.startInternal");
|
|
|
| ChildProcessConnection allocatedConnection = null;
|
| String packageName = creationParams != null ? creationParams.getPackageName()
|
| : context.getPackageName();
|
| - synchronized (ChildProcessLauncher.class) {
|
| + synchronized (sSpareConnectionLock) {
|
| if (inSandbox && sSpareSandboxedConnection != null
|
| && sSpareSandboxedConnection.getPackageName().equals(packageName)) {
|
| + while (sSpareConnectionStarting) {
|
| + try {
|
| + sSpareConnectionLock.wait();
|
| + } catch (InterruptedException ex) {
|
| + }
|
| + }
|
| allocatedConnection = sSpareSandboxedConnection;
|
| sSpareSandboxedConnection = null;
|
| }
|
| @@ -680,15 +719,39 @@ public class ChildProcessLauncher {
|
| if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true;
|
| PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(
|
| context, packageName, inSandbox);
|
| + ChildProcessConnection.StartCallback startCallback =
|
| + new ChildProcessConnection.StartCallback() {
|
| + @Override
|
| + public void onChildStarted() {}
|
| +
|
| + @Override
|
| + public void onChildStartFailed() {
|
| + Log.e(TAG, "ChildProcessConnection.start failed, trying again");
|
| + AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + // The child process may already be bound to another client
|
| + // (this can happen if multi-process WebView is used in more
|
| + // than one process), so try starting the process again.
|
| + // This connection that failed to start has not been freed,
|
| + // so a new bound connection will be allocated.
|
| + startInternal(context, commandLine, childProcessId,
|
| + filesToBeMapped, clientContext, callbackType,
|
| + inSandbox, creationParams);
|
| + }
|
| + });
|
| + }
|
| + };
|
| synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| allocatedConnection = allocateBoundConnection(
|
| - context, commandLine, inSandbox, alwaysInForeground, creationParams);
|
| + context, commandLine, inSandbox, alwaysInForeground, creationParams,
|
| + startCallback);
|
| if (allocatedConnection == null) {
|
| Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn.");
|
| pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine,
|
| childProcessId, filesToBeMapped, clientContext,
|
| callbackType, inSandbox, creationParams));
|
| - return;
|
| + return null;
|
| }
|
| }
|
| }
|
| @@ -697,6 +760,7 @@ public class ChildProcessLauncher {
|
| allocatedConnection.getServiceNumber());
|
| triggerConnectionSetup(allocatedConnection, commandLine, childProcessId,
|
| filesToBeMapped, callbackType, clientContext);
|
| + return allocatedConnection;
|
| } finally {
|
| TraceEvent.end("ChildProcessLauncher.startInternal");
|
| }
|
| @@ -834,9 +898,16 @@ public class ChildProcessLauncher {
|
| }
|
|
|
| @VisibleForTesting
|
| + public static ChildProcessConnection startForTesting(Context context, String[] commandLine,
|
| + FileDescriptorInfo[] filesToMap, ChildProcessCreationParams params) {
|
| + return startInternal(context, commandLine, 0, filesToMap, 0,
|
| + CALLBACK_FOR_RENDERER_PROCESS, true, params);
|
| + }
|
| +
|
| + @VisibleForTesting
|
| static ChildProcessConnection allocateBoundConnectionForTesting(Context context,
|
| ChildProcessCreationParams creationParams) {
|
| - return allocateBoundConnection(context, null, true, false, creationParams);
|
| + return allocateBoundConnection(context, null, true, false, creationParams, null);
|
| }
|
|
|
| @VisibleForTesting
|
| @@ -874,6 +945,14 @@ public class ChildProcessLauncher {
|
| .allocatedConnectionsCountForTesting();
|
| }
|
|
|
| + /**
|
| + * @return the service map of connected services
|
| + */
|
| + @VisibleForTesting
|
| + static Map<Integer, ChildProcessConnection> getServiceMapForTesting() {
|
| + return sServiceMap;
|
| + }
|
| +
|
| /** @return the count of services set up and working */
|
| @VisibleForTesting
|
| static int connectedServicesCountForTesting() {
|
|
|