| 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() {
 | 
| 
 |