Upstream ChildProcessLauncher changes for Android.

Includes updates to SandboxedProcess* to support passing multiple FDs to child
processes.


Review URL: https://chromiumcodereview.appspot.com/10696025

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144801 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/android/jni_array.cc b/base/android/jni_array.cc
index 0a55cc5f..53027c72 100644
--- a/base/android/jni_array.cc
+++ b/base/android/jni_array.cc
@@ -114,5 +114,18 @@
   AppendJavaByteArrayToByteVector(env, byte_array, out);
 }
 
+void JavaIntArrayToIntVector(JNIEnv* env,
+                             jintArray array,
+                             std::vector<int>* out) {
+  DCHECK(out);
+  out->clear();
+  jsize len = env->GetArrayLength(array);
+  jint* ints = env->GetIntArrayElements(array, NULL);
+  for (jsize i = 0; i < len; ++i) {
+    out->push_back(static_cast<int>(ints[i]));
+  }
+  env->ReleaseIntArrayElements(array, ints, JNI_ABORT);
+}
+
 }  // namespace android
 }  // namespace base
diff --git a/base/android/jni_array.h b/base/android/jni_array.h
index 35e3cbbf..e9f6213 100644
--- a/base/android/jni_array.h
+++ b/base/android/jni_array.h
@@ -49,6 +49,11 @@
                                jbyteArray byte_array,
                                std::vector<uint8>* out);
 
+// Replaces the content of |out| with the Java ints in |int_array|.
+void JavaIntArrayToIntVector(JNIEnv* env,
+                             jintArray int_array,
+                             std::vector<int>* out);
+
 }  // namespace android
 }  // namespace base
 
diff --git a/content/app/android/sandboxed_process_service.cc b/content/app/android/sandboxed_process_service.cc
index 7c87d71..58a2687 100644
--- a/content/app/android/sandboxed_process_service.cc
+++ b/content/app/android/sandboxed_process_service.cc
@@ -4,6 +4,7 @@
 
 #include "content/app/android/sandboxed_process_service.h"
 
+#include "base/android/jni_array.h"
 #include "base/global_descriptors_posix.h"
 #include "base/logging.h"
 #include "content/common/android/surface_texture_peer.h"
@@ -14,6 +15,7 @@
 
 using base::android::AttachCurrentThread;
 using base::android::CheckException;
+using base::android::JavaIntArrayToIntVector;
 
 namespace {
 
@@ -35,7 +37,7 @@
                                            int secondary_id) {
     JNIEnv* env = base::android::AttachCurrentThread();
     content::Java_SandboxedProcessService_establishSurfaceTexturePeer(
-       env, service_,  pid, type, j_surface_texture, primary_id, secondary_id);
+        env, service_, pid, type, j_surface_texture, primary_id, secondary_id);
     CheckException(env);
   }
 
@@ -49,18 +51,22 @@
 // Chrome actually uses the renderer code path for all of its sandboxed
 // processes such as renderers, plugins, etc.
 void InternalInitSandboxedProcess(int ipc_fd,
-                                  int crash_fd,
+                                  const std::vector<int>& extra_file_ids,
+                                  const std::vector<int>& extra_file_fds,
                                   JNIEnv* env,
                                   jclass clazz,
                                   jobject context,
                                   jobject service) {
   // Set up the IPC file descriptor mapping.
   base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel, ipc_fd);
-#if defined(USE_LINUX_BREAKPAD)
-  if (crash_fd > 0) {
-    base::GlobalDescriptors::GetInstance()->Set(kCrashDumpSignal, crash_fd);
+  // Register the extra file descriptors.
+  // This usually include the crash dump signals and resource related files.
+  DCHECK(extra_file_fds.size() == extra_file_ids.size());
+  for (size_t i = 0; i < extra_file_ids.size(); ++i) {
+    base::GlobalDescriptors::GetInstance()->Set(extra_file_ids[i],
+                                                extra_file_fds[i]);
   }
-#endif
+
   content::SurfaceTexturePeer::InitInstance(
       new SurfaceTexturePeerSandboxedImpl(service));
 
@@ -75,13 +81,16 @@
                           jobject context,
                           jobject service,
                           jint ipc_fd,
-                          jint crash_fd) {
-  InternalInitSandboxedProcess(static_cast<int>(ipc_fd),
-      static_cast<int>(crash_fd), env, clazz, context, service);
+                          jintArray j_extra_file_ids,
+                          jintArray j_extra_file_fds) {
+  std::vector<int> extra_file_ids;
+  std::vector<int> extra_file_fds;
+  JavaIntArrayToIntVector(env, j_extra_file_ids, &extra_file_ids);
+  JavaIntArrayToIntVector(env, j_extra_file_fds, &extra_file_fds);
 
-  // sandboxed process can't be reused. There is no need to wait for the browser
-  // to unbind the service. Just exit and done.
-  LOG(INFO) << "SandboxedProcessService: Drop out of SandboxedProcessMain.";
+  InternalInitSandboxedProcess(static_cast<int>(ipc_fd),
+                               extra_file_ids, extra_file_fds,
+                               env, clazz, context, service);
 }
 
 void ExitSandboxedProcess(JNIEnv* env, jclass clazz) {
diff --git a/content/browser/android/sandboxed_process_launcher.cc b/content/browser/android/sandboxed_process_launcher.cc
index ca660c9..d1be764 100644
--- a/content/browser/android/sandboxed_process_launcher.cc
+++ b/content/browser/android/sandboxed_process_launcher.cc
@@ -7,11 +7,13 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
 #include "jni/sandboxed_process_launcher_jni.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ToJavaArrayOfStrings;
 using base::android::ScopedJavaLocalRef;
+using base::GlobalDescriptors;
 using content::StartSandboxedProcessCallback;
 
 // Called from SandboxedProcessLauncher.java when the SandboxedProcess was
@@ -31,10 +33,10 @@
 
 namespace content {
 
-ScopedJavaLocalRef<jobject> StartSandboxedProcess(
+void StartSandboxedProcess(
     const CommandLine::StringVector& argv,
     int ipc_fd,
-    int crash_fd,
+    const GlobalDescriptors::Mapping& files_to_register,
     const StartSandboxedProcessCallback& callback) {
   JNIEnv* env = AttachCurrentThread();
   DCHECK(env);
@@ -42,20 +44,26 @@
   // Create the Command line String[]
   ScopedJavaLocalRef<jobjectArray> j_argv = ToJavaArrayOfStrings(env, argv);
 
-  return Java_SandboxedProcessLauncher_start(env,
+  ScopedJavaLocalRef<jintArray> j_file_to_register_id_files(env,
+      env->NewIntArray(files_to_register.size() * 2));
+  scoped_array<jint> file_to_register_id_files(
+      new jint[files_to_register.size() * 2]);
+  for (size_t i = 0; i < files_to_register.size(); ++i) {
+    const GlobalDescriptors::KeyFDPair& id_file = files_to_register[i];
+    file_to_register_id_files[2 * i] = id_file.first;
+    file_to_register_id_files[(2 * i) + 1] = id_file.second;
+  }
+  env->SetIntArrayRegion(j_file_to_register_id_files.obj(),
+                         0, files_to_register.size() * 2,
+                         file_to_register_id_files.get());
+  Java_SandboxedProcessLauncher_start(env,
           base::android::GetApplicationContext(),
           static_cast<jobjectArray>(j_argv.obj()),
           static_cast<jint>(ipc_fd),
-          static_cast<jint>(crash_fd),
+          j_file_to_register_id_files.obj(),
           reinterpret_cast<jint>(new StartSandboxedProcessCallback(callback)));
 }
 
-void CancelStartSandboxedProcess(
-    const base::android::JavaRef<jobject>& connection) {
-  Java_SandboxedProcessLauncher_cancelStart(AttachCurrentThread(),
-                                            connection.obj());
-}
-
 void StopSandboxedProcess(base::ProcessHandle handle) {
   JNIEnv* env = AttachCurrentThread();
   DCHECK(env);
diff --git a/content/browser/android/sandboxed_process_launcher.h b/content/browser/android/sandboxed_process_launcher.h
index 3184780..13b36da 100644
--- a/content/browser/android/sandboxed_process_launcher.h
+++ b/content/browser/android/sandboxed_process_launcher.h
@@ -7,9 +7,10 @@
 
 #include <jni.h>
 
-#include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/global_descriptors_posix.h"
+#include "base/platform_file.h"
 #include "base/process.h"
 
 namespace content {
@@ -17,22 +18,14 @@
 typedef base::Callback<void(base::ProcessHandle)> StartSandboxedProcessCallback;
 // Starts a process as a sandboxed process spawned by the Android
 // ActivityManager.
-// The connection object returned may be used with a subsequent call to
-// CancelStartSandboxedProcess().
 // The created process handle is returned to the |callback| on success, 0 is
 // retuned if the process could not be created.
-base::android::ScopedJavaLocalRef<jobject> StartSandboxedProcess(
+void StartSandboxedProcess(
     const CommandLine::StringVector& argv,
     int ipc_fd,
-    int crash_fd,
+    const base::GlobalDescriptors::Mapping& files_to_register,
     const StartSandboxedProcessCallback& callback);
 
-// Cancel the starting of a sanboxed process.
-//
-// |connection| is the one returned by StartSandboxedProcess.
-void CancelStartSandboxedProcess(
-    const base::android::JavaRef<jobject>& connection);
-
 // Stops a sandboxed process based on the handle returned form
 // StartSandboxedProcess.
 void StopSandboxedProcess(base::ProcessHandle handle);
@@ -44,3 +37,4 @@
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ANDROID_SANDBOXED_PROCESS_LAUNCHER_H_
+
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 094ab1b..fb5ed467 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -25,6 +25,9 @@
 #include "content/common/sandbox_policy.h"
 #elif defined(OS_MACOSX)
 #include "content/browser/mach_broker_mac.h"
+#elif defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "content/browser/android/sandboxed_process_launcher.h"
 #elif defined(OS_POSIX)
 #include "base/memory/singleton.h"
 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
@@ -64,6 +67,8 @@
   void Launch(
 #if defined(OS_WIN)
       const FilePath& exposed_dir,
+#elif defined(OS_ANDROID)
+      int ipcfd,
 #elif defined(OS_POSIX)
       bool use_zygote,
       const base::EnvironmentVector& environ,
@@ -75,6 +80,12 @@
 
     CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
 
+#if defined(OS_ANDROID)
+    // We need to close the client end of the IPC channel to reliably detect
+    // child termination. We will close this fd after we create the child
+    // process which is asynchronous on Android.
+    ipcfd_ = ipcfd;
+#endif
     BrowserThread::PostTask(
         BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
         base::Bind(
@@ -83,6 +94,8 @@
             client_thread_id_,
 #if defined(OS_WIN)
             exposed_dir,
+#elif defined(OS_ANDROID)
+            ipcfd,
 #elif defined(OS_POSIX)
             use_zygote,
             environ,
@@ -91,6 +104,27 @@
             cmd_line));
   }
 
+#if defined(OS_ANDROID)
+  static void OnSandboxedProcessStarted(
+      // |this_object| is NOT thread safe. Only use it to post a task back.
+      scoped_refptr<Context> this_object,
+      BrowserThread::ID client_thread_id,
+      base::ProcessHandle handle) {
+    if (BrowserThread::CurrentlyOn(client_thread_id)) {
+      // This is always invoked on the UI thread which is commonly the
+      // |client_thread_id| so we can shortcut one PostTask.
+      this_object->Notify(handle);
+    } else {
+      BrowserThread::PostTask(
+          client_thread_id, FROM_HERE,
+          base::Bind(
+              &ChildProcessLauncher::Context::Notify,
+              this_object,
+              handle));
+    }
+  }
+#endif
+
   void ResetClient() {
     // No need for locking as this function gets called on the same thread that
     // client_ would be used.
@@ -116,6 +150,8 @@
       BrowserThread::ID client_thread_id,
 #if defined(OS_WIN)
       const FilePath& exposed_dir,
+#elif defined(OS_ANDROID)
+      int ipcfd,
 #elif defined(OS_POSIX)
       bool use_zygote,
       const base::EnvironmentVector& env,
@@ -124,10 +160,25 @@
       CommandLine* cmd_line) {
     scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
 
-    base::ProcessHandle handle = base::kNullProcessHandle;
 #if defined(OS_WIN)
-    handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir);
+    base::ProcessHandle handle = sandbox::StartProcessWithAccess(
+        cmd_line, exposed_dir);
+#elif defined(OS_ANDROID)
+    std::string process_type =
+        cmd_line->GetSwitchValueASCII(switches::kProcessType);
+    base::GlobalDescriptors::Mapping files_to_register;
+    files_to_register.push_back(std::pair<base::GlobalDescriptors::Key, int>(
+        kPrimaryIPCChannel, ipcfd));
+    content::GetContentClient()->browser()->
+        GetAdditionalMappedFilesForChildProcess(*cmd_line, &files_to_register);
+
+    content::StartSandboxedProcess(cmd_line->argv(),
+        ipcfd, files_to_register,
+        base::Bind(&ChildProcessLauncher::Context::OnSandboxedProcessStarted,
+                   this_object, client_thread_id));
+
 #elif defined(OS_POSIX)
+    base::ProcessHandle handle = base::kNullProcessHandle;
     // We need to close the client end of the IPC channel
     // to reliably detect child termination.
     file_util::ScopedFD ipcfd_closer(&ipcfd);
@@ -137,7 +188,7 @@
     base::GlobalDescriptors::Mapping files_to_register;
     files_to_register.push_back(std::pair<base::GlobalDescriptors::Key, int>(
         kPrimaryIPCChannel, ipcfd));
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
     content::GetContentClient()->browser()->
         GetAdditionalMappedFilesForChildProcess(*cmd_line, &files_to_register);
     if (use_zygote) {
@@ -146,7 +197,7 @@
                                                           process_type);
     } else
     // Fall through to the normal posix case below when we're not zygoting.
-#endif  // defined(OS_MACOSX) && !defined(OS_ANDROID)
+#endif  // !defined(OS_MACOSX)
     {
       // Convert FD mapping to FileHandleMappingVector
       base::FileHandleMappingVector fds_to_map;
@@ -158,7 +209,7 @@
             id_file.first + base::GlobalDescriptors::kBaseDescriptor));
       }
 
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
       if (process_type == switches::kRendererProcess) {
         const int sandbox_fd =
             RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
@@ -166,7 +217,7 @@
             sandbox_fd,
             kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
       }
-#endif  // defined(OS_MACOSX) && !defined(OS_ANDROID)
+#endif  // defined(OS_MACOSX)
 
       // Actually launch the app.
       base::LaunchOptions options;
@@ -204,16 +255,17 @@
         handle = base::kNullProcessHandle;
     }
 #endif  // else defined(OS_POSIX)
-
-    BrowserThread::PostTask(
-        client_thread_id, FROM_HERE,
-        base::Bind(
-            &Context::Notify,
-            this_object.get(),
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
-            use_zygote,
+#if !defined(OS_ANDROID)
+  BrowserThread::PostTask(
+      client_thread_id, FROM_HERE,
+      base::Bind(
+          &Context::Notify,
+          this_object.get(),
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+          use_zygote,
 #endif
-            handle));
+          handle));
+#endif  // !defined(OS_ANDROID)
   }
 
   void Notify(
@@ -221,6 +273,10 @@
       bool zygote,
 #endif
       base::ProcessHandle handle) {
+#if defined(OS_ANDROID)
+    // Finally close the ipcfd
+    file_util::ScopedFD ipcfd_closer(&ipcfd_);
+#endif
     starting_ = false;
     process_.set_handle(handle);
     if (!handle)
@@ -267,13 +323,17 @@
       bool zygote,
 #endif
       base::ProcessHandle handle) {
+#if defined(OS_ANDROID)
+    LOG(INFO) << "ChromeProcess: Stopping process with handle " << handle;
+    content::StopSandboxedProcess(handle);
+#else
     base::Process process(handle);
      // Client has gone away, so just kill the process.  Using exit code 0
     // means that UMA won't treat this as a crash.
     process.Terminate(content::RESULT_CODE_NORMAL_EXIT);
     // On POSIX, we must additionally reap the child.
 #if defined(OS_POSIX)
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
     if (zygote) {
       // If the renderer was created via a zygote, we have to proxy the reaping
       // through the zygote process.
@@ -285,6 +345,7 @@
     }
 #endif  // OS_POSIX
     process.Close();
+#endif  // defined(OS_ANDROID)
   }
 
   Client* client_;
@@ -296,8 +357,10 @@
   // Controls whether the child process should be terminated on browser
   // shutdown. Default behavior is to terminate the child.
   bool terminate_child_on_shutdown_;
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#if defined(OS_ANDROID)
+  // The fd to close after creating the process.
+  int ipcfd_;
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
   bool zygote_;
 #endif
 };
@@ -317,6 +380,8 @@
   context_->Launch(
 #if defined(OS_WIN)
       exposed_dir,
+#elif defined(OS_ANDROID)
+      ipcfd,
 #elif defined(OS_POSIX)
       use_zygote,
       environ,
diff --git a/content/public/android/java/src/org/chromium/content/app/LibraryLoader.java b/content/public/android/java/src/org/chromium/content/app/LibraryLoader.java
index 963cdd2..33a4937 100644
--- a/content/public/android/java/src/org/chromium/content/app/LibraryLoader.java
+++ b/content/public/android/java/src/org/chromium/content/app/LibraryLoader.java
@@ -21,9 +21,7 @@
 public class LibraryLoader {
     private static final String TAG = "LibraryLoader";
 
-    /* TODO(jrg): resolve up and downstream discrepancy; there is no
-     * upstream libchromeview.so */
-    private static String sLibrary = "chromeview";
+    private static String sLibrary = null;
 
     private static boolean sLoaded = false;
 
@@ -62,6 +60,13 @@
     }
 
     /**
+     * @return The name of the native library set to be loaded.
+     */
+    public static String getLibraryToLoad() {
+        return sLibrary;
+    }
+
+    /**
      *  This method blocks until the library is fully loaded and initialized;
      *  must be called on the thread that the native will call its "main" thread.
      */
@@ -168,6 +173,10 @@
      * @return Whether the native library was successfully loaded.
      */
     static boolean loadNow() {
+        if (sLibrary == null) {
+            assert false : "No library specified to load.  Call setLibraryToLoad before first.";
+            return false;
+        }
         assert !sInitialized;
         try {
             Log.i(TAG, "loading: " + sLibrary);
diff --git a/content/public/android/java/src/org/chromium/content/app/SandboxedProcessService.java b/content/public/android/java/src/org/chromium/content/app/SandboxedProcessService.java
index 2a3c2bd..4ce68ed 100644
--- a/content/public/android/java/src/org/chromium/content/app/SandboxedProcessService.java
+++ b/content/public/android/java/src/org/chromium/content/app/SandboxedProcessService.java
@@ -16,6 +16,8 @@
 import android.util.Log;
 import android.view.Surface;
 
+import java.util.ArrayList;
+
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.content.app.ContentMain;
@@ -43,9 +45,12 @@
     // This is the native "Main" thread for the renderer / utility process.
     private Thread mSandboxMainThread;
     // Parameters received via IPC, only accessed while holding the mSandboxMainThread monitor.
+    private String mNativeLibraryName;  // Must be passed in via the bind command.
     private String[] mCommandLineParams;
     private ParcelFileDescriptor mIPCFd;
-    private ParcelFileDescriptor mCrashFd;
+    // Pairs IDs and file descriptors that should be registered natively.
+    private ArrayList<Integer> mExtraFileIds;
+    private ArrayList<ParcelFileDescriptor> mExtraFileFds;
 
     private static Context sContext = null;
     private boolean mLibraryInitialized = false;
@@ -66,9 +71,20 @@
                 // We must have received the command line by now
                 assert mCommandLineParams != null;
                 mIPCFd = args.getParcelable(SandboxedProcessConnection.EXTRA_IPC_FD);
-                // mCrashFd may be null if native crash reporting is disabled.
-                if (args.containsKey(SandboxedProcessConnection.EXTRA_CRASH_FD)) {
-                    mCrashFd = args.getParcelable(SandboxedProcessConnection.EXTRA_CRASH_FD);
+                mExtraFileIds = new ArrayList<Integer>();
+                mExtraFileFds = new ArrayList<ParcelFileDescriptor>();
+                for (int i = 0;; i++) {
+                    String fdName = SandboxedProcessConnection.EXTRA_FILES_PREFIX + i
+                            + SandboxedProcessConnection.EXTRA_FILES_FD_SUFFIX;
+                    ParcelFileDescriptor parcel = args.getParcelable(fdName);
+                    if (parcel == null) {
+                        // End of the file list.
+                        break;
+                    }
+                    mExtraFileFds.add(parcel);
+                    String idName = SandboxedProcessConnection.EXTRA_FILES_PREFIX + i
+                            + SandboxedProcessConnection.EXTRA_FILES_ID_SUFFIX;
+                    mExtraFileIds.add(args.getInt(idName));
                 }
                 mSandboxMainThread.notifyAll();
             }
@@ -95,6 +111,12 @@
             @Override
             public void run()  {
                 try {
+                    synchronized (mSandboxMainThread) {
+                        while (mNativeLibraryName == null) {
+                            mSandboxMainThread.wait();
+                        }
+                    }
+                    LibraryLoader.setLibraryToLoad(mNativeLibraryName);
                     if (!LibraryLoader.loadNow()) return;
                     synchronized (mSandboxMainThread) {
                         while (mCommandLineParams == null) {
@@ -109,10 +131,17 @@
                             mSandboxMainThread.wait();
                         }
                     }
-                    int crashFd = (mCrashFd == null) ? -1 : mCrashFd.detachFd();
+                    assert mExtraFileIds.size() == mExtraFileFds.size();
+                    int[] extraFileIds = new int[mExtraFileIds.size()];
+                    int[] extraFileFds = new int[mExtraFileFds.size()];
+                    for (int i = 0; i < mExtraFileIds.size(); ++i) {
+                        extraFileIds[i] = mExtraFileIds.get(i);
+                        extraFileFds[i] = mExtraFileFds.get(i).detachFd();
+                    }
                     ContentMain.initApplicationContext(sContext.getApplicationContext());
                     nativeInitSandboxedProcess(sContext.getApplicationContext(),
-                            SandboxedProcessService.this, mIPCFd.detachFd(), crashFd);
+                            SandboxedProcessService.this, mIPCFd.detachFd(),
+                            extraFileIds, extraFileFds);
                     ContentMain.start();
                     nativeExitSandboxedProcess();
                 } catch (InterruptedException e) {
@@ -158,6 +187,8 @@
         stopSelf();
 
         synchronized (mSandboxMainThread) {
+            mNativeLibraryName = intent.getStringExtra(
+                    SandboxedProcessConnection.EXTRA_NATIVE_LIBRARY_NAME);
             mCommandLineParams = intent.getStringArrayExtra(
                     SandboxedProcessConnection.EXTRA_COMMAND_LINE);
             mSandboxMainThread.notifyAll();
@@ -179,8 +210,8 @@
      */
     @SuppressWarnings("unused")
     @CalledByNative
-    private void establishSurfaceTexturePeer(int pid, int type, Object surfaceObject, int primaryID,
-                                             int secondaryID) {
+    private void establishSurfaceTexturePeer(
+            int pid, int type, Object surfaceObject, int primaryID, int secondaryID) {
         if (mCallback == null) {
             Log.e(TAG, "No callback interface has been provided.");
             return;
@@ -216,10 +247,13 @@
      * @param applicationContext The Application Context of the current process.
      * @param service The current SandboxedProcessService object.
      * @param ipcFd File descriptor to use for ipc.
-     * @param crashFd File descriptor for signaling crashes.
+     * @param extraFileIds (Optional) A list of pair of file IDs that should be registered for
+     *                     access by the renderer.
+     * @param extraFileFds (Optional) A list of pair of file descriptors that should be registered
+     *                     for access by the renderer.
      */
     private static native void nativeInitSandboxedProcess(Context applicationContext,
-            SandboxedProcessService service, int ipcFd, int crashFd);
+            SandboxedProcessService service, int ipcFd, int[] extraFileIds, int[] extraFileFds);
 
     /**
      * Force the sandboxed process to exit.
diff --git a/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessConnection.java
index df6af2b..e6376b8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessConnection.java
@@ -16,31 +16,32 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.chromium.base.CalledByNative;
-import org.chromium.content.app.SandboxedProcessService;
+import org.chromium.base.ThreadUtils;
 import org.chromium.content.common.CommandLine;
 import org.chromium.content.common.ISandboxedProcessCallback;
 import org.chromium.content.common.ISandboxedProcessService;
 import org.chromium.content.common.TraceEvent;
 
-public class SandboxedProcessConnection {
+public class SandboxedProcessConnection implements ServiceConnection {
     interface DeathCallback {
         void onSandboxedProcessDied(int pid);
     }
 
     // Names of items placed in the bind intent or connection bundle.
     public static final String EXTRA_COMMAND_LINE =
-        "com.google.android.apps.chrome.extra.sandbox_command_line";
+            "com.google.android.apps.chrome.extra.sandbox_command_line";
+    public static final String EXTRA_NATIVE_LIBRARY_NAME =
+            "com.google.android.apps.chrome.extra.sandbox_native_library_name";
     // Note the FD may only be passed in the connection bundle.
     public static final String EXTRA_IPC_FD = "com.google.android.apps.chrome.extra.sandbox_ipcFd";
-    public static final String EXTRA_CRASH_FD =
-        "com.google.android.apps.chrome.extra.sandbox_crashFd";
-    public static final String EXTRA_CHROME_PAK_FD =
-        "com.google.android.apps.chrome.extra.sandbox_chromePakFd";
-    public static final String EXTRA_LOCALE_PAK_FD =
-        "com.google.android.apps.chrome.extra.sandbox_localePakFd";
+    public static final String EXTRA_FILES_PREFIX =
+            "com.google.android.apps.chrome.extra.sandbox_extraFile_";
+    public static final String EXTRA_FILES_ID_SUFFIX = "_id";
+    public static final String EXTRA_FILES_FD_SUFFIX = "_fd";
 
     private final Context mContext;
     private final int mServiceNumber;
@@ -61,99 +62,27 @@
     private static class ConnectionParams {
         final String[] mCommandLine;
         final int mIpcFd;
-        final int mCrashFd;
+        final int[] mExtraFileIdsAndFds;
         final ISandboxedProcessCallback mCallback;
         final Runnable mOnConnectionCallback;
 
         ConnectionParams(
                 String[] commandLine,
                 int ipcFd,
-                int crashFd,
+                int[] idsAndFds,
                 ISandboxedProcessCallback callback,
                 Runnable onConnectionCallback) {
             mCommandLine = commandLine;
             mIpcFd = ipcFd;
-            mCrashFd = crashFd;
+            mExtraFileIdsAndFds = idsAndFds;
             mCallback = callback;
             mOnConnectionCallback = onConnectionCallback;
         }
-    };
-
-    // Implement the ServiceConnection as an inner class, so it can stem the service
-    // callbacks when cancelled or unbound.
-    class AsyncBoundServiceConnection extends AsyncTask<Intent, Void, Boolean>
-            implements ServiceConnection {
-        private boolean mIsDestroyed = false;
-        private AtomicBoolean mIsBound = new AtomicBoolean(false);
-
-        // AsyncTask
-        @Override
-        protected Boolean doInBackground(Intent... intents) {
-            boolean isBound = mContext.bindService(intents[0], this, Context.BIND_AUTO_CREATE);
-            mIsBound.set(isBound);
-            return isBound;
-        }
-
-        @Override
-        protected void onPostExecute(Boolean boundOK) {
-            synchronized (SandboxedProcessConnection.this) {
-                if (!boundOK && !mIsDestroyed) {
-                    SandboxedProcessConnection.this.onBindFailed();
-                }
-                // else: bind will complete asynchronously with a callback to onServiceConnected().
-            }
-        }
-
-        @Override
-        protected void onCancelled(Boolean boundOK) {
-            // According to {@link AsyncTask#onCancelled(Object)}, the Object can be null.
-            if (boundOK != null && boundOK) {
-                unBindIfAble();
-            }
-        }
-
-        /**
-         * Unbinds this connection if it hasn't already been unbound. There's a guard to check that
-         * we haven't already been unbound because the Browser process cancelling a connection can
-         * race with something else (Android?) cancelling the connection.
-         */
-        private void unBindIfAble() {
-            if (mIsBound.getAndSet(false)) {
-                mContext.unbindService(this);
-            }
-        }
-
-        // ServiceConnection
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (SandboxedProcessConnection.this) {
-                if (!mIsDestroyed) {
-                    SandboxedProcessConnection.this.onServiceConnected(name, service);
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            synchronized (SandboxedProcessConnection.this) {
-                if (!mIsDestroyed) {
-                    SandboxedProcessConnection.this.onServiceDisconnected(name);
-                }
-            }
-        }
-
-        public void destroy() {
-            assert Thread.holdsLock(SandboxedProcessConnection.this);
-            if (!cancel(false)) {
-                unBindIfAble();
-            }
-            mIsDestroyed = true;
-        }
     }
 
     // This is only valid while the connection is being established.
     private ConnectionParams mConnectionParams;
-    private AsyncBoundServiceConnection mServiceConnection;
+    private boolean mIsBound;
 
     SandboxedProcessConnection(Context context, int number,
             SandboxedProcessConnection.DeathCallback deathCallback) {
@@ -172,7 +101,7 @@
 
     private Intent createServiceBindIntent() {
         Intent intent = new Intent();
-        String n = SandboxedProcessService.class.getName();
+        String n = org.chromium.content.app.SandboxedProcessService.class.getName();
         intent.setClassName(mContext, n + mServiceNumber);
         intent.setPackage(mContext.getPackageName());
         return intent;
@@ -183,37 +112,25 @@
      * to setup the connection parameters. (These methods are separated to allow the client
      * to pass whatever parameters they have available here, and complete the remainder
      * later while reducing the connection setup latency).
-     *
+     * @param nativeLibraryName The name of the shared native library to be loaded for the
+     *                          sandboxed process.
      * @param commandLine (Optional) Command line for the sandboxed process. If omitted, then
-     * the command line parameters must instead be passed to setupConnection().
+     *                    the command line parameters must instead be passed to setupConnection().
      */
-    synchronized void bind(String[] commandLine) {
+    synchronized void bind(String nativeLibraryName, String[] commandLine) {
         TraceEvent.begin();
+        assert !ThreadUtils.runningOnUiThread();
+
         final Intent intent = createServiceBindIntent();
 
+        intent.putExtra(EXTRA_NATIVE_LIBRARY_NAME, nativeLibraryName);
         if (commandLine != null) {
             intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
         }
-        // TODO(joth): By the docs, AsyncTasks should only be created on the UI thread, but
-        // bind() currently 'may' be called on any thread. In practice it's only ever called
-        // from UI, but it's not guaranteed. See http://b/5694925.
-        Looper mainLooper = Looper.getMainLooper();
-        if (Looper.myLooper() == mainLooper) {
-            mServiceConnection = new AsyncBoundServiceConnection();
-            // On completion this will call back to onServiceConnected().
-            mServiceConnection.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, intent);
-        } else {
-            // TODO(jcivelli): http://b/5694925 we only have to post to the UI thread because we use
-            // an AsyncTask and it requires it. Replace that AsyncTask by running our own thread and
-            // change this so we run directly from the current thread.
-            new Handler(mainLooper).postAtFrontOfQueue(new Runnable() {
-                    public void run() {
-                            mServiceConnection = new AsyncBoundServiceConnection();
-                            // On completion this will call back to onServiceConnected().
-                            mServiceConnection.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
-                                    intent);
-                    }
-            });
+
+        mIsBound = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
+        if (!mIsBound) {
+            onBindFailed();
         }
         TraceEvent.end();
     }
@@ -223,20 +140,21 @@
      * This establishes the parameters that were not already supplied in bind.
      * @param commandLine (Optional) will be ignored if the command line was already sent in bind()
      * @param ipcFd The file descriptor that will be used by the sandbox process for IPC.
-     * @param crashFd (Optional) file descriptor that will be used for crash dumps.
+     * @param fileToRegisterIdFds (Optional)  a list of pair of IDs and FDs that should be
+     *                            registered
      * @param callback Used for status updates regarding this process connection.
      * @param onConnectionCallback will be run when the connection is setup and ready to use.
      */
     synchronized void setupConnection(
             String[] commandLine,
             int ipcFd,
-            int crashFd,
+            int[] fileToRegisterIdFds,
             ISandboxedProcessCallback callback,
             Runnable onConnectionCallback) {
         TraceEvent.begin();
         assert mConnectionParams == null;
-        mConnectionParams = new ConnectionParams(commandLine, ipcFd, crashFd, callback,
-                                                 onConnectionCallback);
+        mConnectionParams = new ConnectionParams(commandLine, ipcFd, fileToRegisterIdFds, callback,
+                onConnectionCallback);
         if (mServiceConnectComplete) {
             doConnectionSetup();
         }
@@ -247,9 +165,9 @@
      * Unbind the ISandboxedProcessService. It is safe to call this multiple times.
      */
     synchronized void unbind() {
-        if (mServiceConnection != null) {
-            mServiceConnection.destroy();
-            mServiceConnection = null;
+        if (mIsBound) {
+            mContext.unbindService(this);
+            mIsBound = false;
         }
         if (mService != null) {
             if (mHighPriorityConnection != null) {
@@ -263,8 +181,8 @@
     }
 
     // Called on the main thread to notify that the service is connected.
-    private void onServiceConnected(ComponentName className, IBinder service) {
-        assert Thread.holdsLock(this);
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
         TraceEvent.begin();
         mServiceConnectComplete = true;
         mService = ISandboxedProcessService.Stub.asInterface(service);
@@ -276,7 +194,6 @@
 
     // Called on the main thread to notify that the bindService() call failed (returned false).
     private void onBindFailed() {
-        assert Thread.holdsLock(this);
         mServiceConnectComplete = true;
         if (mConnectionParams != null) {
             doConnectionSetup();
@@ -296,30 +213,43 @@
         if (onConnectionCallback == null) {
             unbind();
         } else if (mService != null) {
+            ParcelFileDescriptor ipcFdParcel;
             try {
-                ParcelFileDescriptor ipcFdParcel =
-                    ParcelFileDescriptor.fromFd(mConnectionParams.mIpcFd);
-                Bundle bundle = new Bundle();
-                bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
-                bundle.putParcelable(EXTRA_IPC_FD, ipcFdParcel);
+                ipcFdParcel = ParcelFileDescriptor.fromFd(mConnectionParams.mIpcFd);
+            } catch(IOException e) {
+                Log.e(TAG, "Invalid IPC FD, aborting connection.", e);
+                return;
+            }
+            Bundle bundle = new Bundle();
+            bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
+            bundle.putParcelable(EXTRA_IPC_FD, ipcFdParcel);
 
+            int[] idsAndFds = mConnectionParams.mExtraFileIdsAndFds;
+            assert idsAndFds.length % 2 == 0;
+            int pairLength = idsAndFds.length / 2;
+            for (int i = 0; i < pairLength; i++) {
+                String idName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_ID_SUFFIX;
+                String fdName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_FD_SUFFIX;
+                ParcelFileDescriptor parcelFile;
                 try {
-                    ParcelFileDescriptor crashFdParcel =
-                        ParcelFileDescriptor.fromFd(mConnectionParams.mCrashFd);
-                    bundle.putParcelable(EXTRA_CRASH_FD, crashFdParcel);
-                  // We will let the GC close the crash ParcelFileDescriptor.
-                } catch (java.io.IOException e) {
-                    Log.w(TAG, "Invalid crash Fd. Native crash reporting will be disabled.");
+                    parcelFile = ParcelFileDescriptor.fromFd(idsAndFds[(2 * i) + 1]);
+                    bundle.putParcelable(fdName, parcelFile);
+                    bundle.putInt(idName, idsAndFds[2 * i]);
+                } catch (IOException e) {
+                    Log.e(TAG, "Invalid extra file FD: id=" + idsAndFds[ 2 * i] + " fd="
+                          + idsAndFds[(2 * i) + 1]);
                 }
-
+            }
+            try {
                 mPID = mService.setupConnection(bundle, mConnectionParams.mCallback);
-                ipcFdParcel.close();  // We proactivley close now rather than wait for GC &
-                                      // finalizer.
-            } catch(java.io.IOException e) {
-                Log.w(TAG, "Invalid ipc FD.");
-            } catch(android.os.RemoteException e) {
-                Log.w(TAG, "Exception when trying to call service method: "
-                        + e);
+            } catch (android.os.RemoteException re) {
+                Log.e(TAG, "Failed to setup connection.", re);
+            }
+            try {
+                // We proactivley close now rather than wait for GC & finalizer.
+                ipcFdParcel.close();
+            } catch (IOException ioe) {
+                Log.w(TAG, "Failed to close IPC FD.", ioe);
             }
         }
         mConnectionParams = null;
@@ -330,8 +260,8 @@
     }
 
     // Called on the main thread to notify that the sandboxed service did not disconnect gracefully.
-    private void onServiceDisconnected(ComponentName className) {
-        assert Thread.holdsLock(this);
+    @Override
+    public void onServiceDisconnected(ComponentName className) {
         int pid = mPID;  // Stash pid & connection callback since unbind() will clear them.
         Runnable onConnectionCallback =
             mConnectionParams != null ? mConnectionParams.mOnConnectionCallback : null;
diff --git a/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessLauncher.java
index 83264c260..3627523 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessLauncher.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SandboxedProcessLauncher.java
@@ -13,6 +13,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.chromium.base.CalledByNative;
+import org.chromium.base.ThreadUtils;
+import org.chromium.content.app.LibraryLoader;
 import org.chromium.content.common.CommandLine;
 import org.chromium.content.common.ISandboxedProcessCallback;
 import org.chromium.content.common.ISandboxedProcessService;
@@ -74,7 +76,10 @@
             String[] commandLine) {
         SandboxedProcessConnection connection = allocateConnection(context);
         if (connection != null) {
-            connection.bind(commandLine);
+            String libraryName = LibraryLoader.getLibraryToLoad();
+            assert libraryName != null : "Attempting to launch a sandbox process without first "
+                    + "calling LibraryLoader.setLibraryToLoad";
+            connection.bind(libraryName, commandLine);
         }
         return connection;
     }
@@ -136,10 +141,11 @@
 
     /**
      * Should be called early in startup so the work needed to spawn the sandboxed process can
-     * be done in parallel to other startup work.
+     * be done in parallel to other startup work. Must not be called on the UI thread.
      * @param context the application context used for the connection.
      */
     public static synchronized void warmUp(Context context) {
+        assert !ThreadUtils.runningOnUiThread();
         if (mSpareConnection == null) {
             mSpareConnection = allocateBoundConnection(context, null);
         }
@@ -155,14 +161,13 @@
      * @param commandLine The sandboxed process command line argv.
      * @param ipcFd File descriptor used to set up IPC.
      * @param clientContext Arbitrary parameter used by the client to distinguish this connection.
-     * @return Connection object which maybe used with subsequent call to {@link #cancelStart}
      */
     @CalledByNative
-    static SandboxedProcessConnection start(
+    static void start(
             Context context,
             final String[] commandLine,
             int ipcFd,
-            int crashFd,
+            int[] fileToRegisterIdFds,
             final int clientContext) {
         assert clientContext != 0;
         SandboxedProcessConnection allocatedConnection;
@@ -173,7 +178,7 @@
         if (allocatedConnection == null) {
             allocatedConnection = allocateBoundConnection(context, commandLine);
             if (allocatedConnection == null) {
-                return null;
+                return;
             }
         }
         final SandboxedProcessConnection connection = allocatedConnection;
@@ -192,21 +197,8 @@
                 nativeOnSandboxedProcessStarted(clientContext, pid);
             }
         };
-        connection.setupConnection(commandLine, ipcFd, crashFd, createCallback(), onConnect);
-        return connection;
-    }
-
-    /**
-     * Cancels a pending connection to a sandboxed process. This may be called from any thread.
-     *
-     * @param connection the object that was returned from the corresponding call to {@link #start}.
-     */
-    @CalledByNative
-    static void cancelStart(SandboxedProcessConnection connection) {
-        assert connection != null;
-        assert !mServiceMap.containsValue(connection);
-        connection.unbind();
-        freeConnection(connection);
+        connection.setupConnection(commandLine, ipcFd, fileToRegisterIdFds, createCallback(),
+                onConnect);
     }
 
     /**