Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java |
index d810afd8769e06992db363bbacc94e45eb8ab9f5..0050f3603e612600ce132cf3665013e596fc89f0 100644 |
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java |
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java |
@@ -5,14 +5,21 @@ |
package org.chromium.content.browser; |
import android.content.Context; |
+import android.os.Bundle; |
+import android.os.IBinder; |
import android.os.ParcelFileDescriptor; |
import org.chromium.base.ContextUtils; |
+import org.chromium.base.CpuFeatures; |
import org.chromium.base.Log; |
+import org.chromium.base.VisibleForTesting; |
import org.chromium.base.annotations.CalledByNative; |
import org.chromium.base.annotations.JNINamespace; |
+import org.chromium.base.library_loader.Linker; |
import org.chromium.base.process_launcher.ChildProcessCreationParams; |
import org.chromium.base.process_launcher.FileDescriptorInfo; |
+import org.chromium.content.app.ChromiumLinkerParams; |
+import org.chromium.content.common.ContentSwitches; |
import java.io.IOException; |
@@ -22,14 +29,19 @@ import java.io.IOException; |
* Each public or jni methods should have explicit documentation on what threads they are called. |
*/ |
@JNINamespace("content::internal") |
-class ChildProcessLauncherHelper { |
+public class ChildProcessLauncherHelper { |
private static final String TAG = "ChildProcLH"; |
// Represents an invalid process handle; same as base/process/process.h kNullProcessHandle. |
private static final int NULL_PROCESS_HANDLE = 0; |
+ // The IBinder provided to the created service. |
+ private final IBinder mIBinderCallback; |
+ |
// Note native pointer is only guaranteed live until nativeOnChildProcessStarted. |
private long mNativeChildProcessLauncherHelper; |
+ |
+ // The actual service connection. Set once we have connected to the service. |
private ChildProcessConnection mChildProcessConnection; |
@CalledByNative |
@@ -51,30 +63,93 @@ class ChildProcessLauncherHelper { |
return new FileDescriptorInfo(id, pFd, offset, size); |
} |
+ @VisibleForTesting |
@CalledByNative |
- private static ChildProcessLauncherHelper create(long nativePointer, int paramId, |
+ public static ChildProcessLauncherHelper createAndStart(long nativePointer, int paramId, |
final String[] commandLine, FileDescriptorInfo[] filesToBeMapped) { |
assert LauncherThread.runningOnLauncherThread(); |
- return new ChildProcessLauncherHelper(nativePointer, paramId, commandLine, filesToBeMapped); |
+ String processType = |
+ ContentSwitches.getSwitchValue(commandLine, ContentSwitches.SWITCH_PROCESS_TYPE); |
+ |
+ ChildProcessCreationParams params = ChildProcessCreationParams.get(paramId); |
+ if (paramId != ChildProcessCreationParams.DEFAULT_ID && params == null) { |
+ throw new RuntimeException("CreationParams id " + paramId + " not found"); |
+ } |
+ |
+ Context context = ContextUtils.getApplicationContext(); |
+ boolean sandboxed = true; |
+ boolean alwaysInForeground = false; |
+ if (!ContentSwitches.SWITCH_RENDERER_PROCESS.equals(processType)) { |
+ if (params != null && !params.getPackageName().equals(context.getPackageName())) { |
+ // WebViews and WebAPKs have renderer processes running in their applications. |
+ // When launching these renderer processes, {@link ManagedChildProcessConnection} |
+ // requires the package name of the application which holds the renderer process. |
+ // Therefore, the package name in ChildProcessCreationParams could be the package |
+ // name of WebViews, WebAPKs, or Chrome, depending on the host application. |
+ // Except renderer process, all other child processes should use Chrome's package |
+ // name. In WebAPK, ChildProcessCreationParams are initialized with WebAPK's |
+ // package name. Make a copy of the WebAPK's params, but replace the package with |
+ // Chrome's package to use when initializing a non-renderer processes. |
+ // TODO(boliu): Should fold into |paramId|. Investigate why this is needed. |
+ params = new ChildProcessCreationParams(context.getPackageName(), |
+ params.getIsExternalService(), params.getLibraryProcessType(), |
+ params.getBindToCallerCheck()); |
+ } |
+ if (ContentSwitches.SWITCH_GPU_PROCESS.equals(processType)) { |
+ sandboxed = false; |
+ alwaysInForeground = true; |
+ } else { |
+ // We only support sandboxed utility processes now. |
+ assert ContentSwitches.SWITCH_UTILITY_PROCESS.equals(processType); |
+ } |
+ } |
+ |
+ ChildProcessLauncherHelper process_launcher = |
+ new ChildProcessLauncherHelper(nativePointer, processType); |
+ process_launcher.start( |
+ context, commandLine, filesToBeMapped, params, sandboxed, alwaysInForeground); |
+ return process_launcher; |
} |
- private ChildProcessLauncherHelper(long nativePointer, int paramId, final String[] commandLine, |
- FileDescriptorInfo[] filesToBeMapped) { |
+ private ChildProcessLauncherHelper(long nativePointer, String processType) { |
assert LauncherThread.runningOnLauncherThread(); |
mNativeChildProcessLauncherHelper = nativePointer; |
+ mIBinderCallback = ContentSwitches.SWITCH_GPU_PROCESS.equals(processType) |
+ ? new GpuProcessCallback() |
+ : null; |
+ initLinker(); |
+ } |
- ChildProcessLauncher.start(ContextUtils.getApplicationContext(), paramId, commandLine, |
- filesToBeMapped, new ChildProcessLauncher.LaunchCallback() { |
+ private void start(Context context, String[] commandLine, |
+ final FileDescriptorInfo[] filesToBeMapped, ChildProcessCreationParams params, |
+ boolean sandboxed, boolean alwaysInForeground) { |
+ boolean bindToCallerCheck = params == null ? false : params.getBindToCallerCheck(); |
+ Bundle serviceBundle = createServiceBundle(bindToCallerCheck); |
+ onBeforeConnectionAllocated(serviceBundle); |
+ |
+ Bundle connectionBundle = createConnectionBundle(commandLine, filesToBeMapped); |
+ ChildProcessLauncher.start(context, serviceBundle, |
+ connectionBundle, new ChildProcessLauncher.LaunchCallback() { |
@Override |
public void onChildProcessStarted(ChildProcessConnection connection) { |
mChildProcessConnection = connection; |
+ |
+ // Proactively close the FDs rather than waiting for the GC to do it. |
+ try { |
+ for (FileDescriptorInfo fileInfo : filesToBeMapped) { |
+ fileInfo.fd.close(); |
+ } |
+ } catch (IOException ioe) { |
+ Log.w(TAG, "Failed to close FD.", ioe); |
+ } |
+ |
if (mNativeChildProcessLauncherHelper != 0) { |
nativeOnChildProcessStarted( |
mNativeChildProcessLauncherHelper, getPid()); |
} |
mNativeChildProcessLauncherHelper = 0; |
} |
- }); |
+ }, getIBinderCallback(), sandboxed, alwaysInForeground, params); |
} |
private int getPid() { |
@@ -130,4 +205,101 @@ class ChildProcessLauncherHelper { |
// Can be called on a number of threads, including launcher, and binder. |
private static native void nativeOnChildProcessStarted( |
long nativeChildProcessLauncherHelper, int pid); |
+ |
+ private static boolean sLinkerInitialized; |
+ private static long sLinkerLoadAddress; |
+ @VisibleForTesting |
+ static void initLinker() { |
+ assert LauncherThread.runningOnLauncherThread(); |
+ if (sLinkerInitialized) return; |
+ if (Linker.isUsed()) { |
+ sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress(); |
+ if (sLinkerLoadAddress == 0) { |
+ Log.i(TAG, "Shared RELRO support disabled!"); |
+ } |
+ } |
+ sLinkerInitialized = true; |
+ } |
+ |
+ private static ChromiumLinkerParams getLinkerParamsForNewConnection() { |
+ assert LauncherThread.runningOnLauncherThread(); |
+ assert sLinkerInitialized; |
+ |
+ if (sLinkerLoadAddress == 0) return null; |
+ |
+ // Always wait for the shared RELROs in service processes. |
+ final boolean waitForSharedRelros = true; |
+ if (Linker.areTestsEnabled()) { |
+ Linker linker = Linker.getInstance(); |
+ return new ChromiumLinkerParams(sLinkerLoadAddress, waitForSharedRelros, |
+ linker.getTestRunnerClassNameForTesting(), |
+ linker.getImplementationForTesting()); |
+ } else { |
+ return new ChromiumLinkerParams(sLinkerLoadAddress, waitForSharedRelros); |
+ } |
+ } |
+ |
+ /** |
+ * Creates the common bundle to be passed to child processes through the service binding intent. |
+ * If the service gets recreated by the framework the intent will be reused, so these parameters |
+ * should be common to all processes of that type. |
+ * |
+ * @param commandLine Command line params to be passed to the service. |
+ * @param linkerParams Linker params to start the service. |
+ */ |
+ // TODO(jcivelli): make private once warmup connection code is move from ChildProcessLauncher to |
+ // this class and remove initLinker call. |
+ static Bundle createServiceBundle(boolean bindToCallerCheck) { |
+ initLinker(); |
+ Bundle bundle = new Bundle(); |
+ bundle.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER, bindToCallerCheck); |
+ bundle.putParcelable( |
+ ChildProcessConstants.EXTRA_LINKER_PARAMS, getLinkerParamsForNewConnection()); |
+ return bundle; |
+ } |
+ |
+ @VisibleForTesting |
+ public static Bundle createConnectionBundle( |
+ String[] commandLine, FileDescriptorInfo[] filesToBeMapped) { |
+ assert sLinkerInitialized; |
+ |
+ Bundle bundle = new Bundle(); |
+ bundle.putStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE, commandLine); |
+ bundle.putParcelableArray(ChildProcessConstants.EXTRA_FILES, filesToBeMapped); |
+ // content specific parameters. |
+ bundle.putInt(ChildProcessConstants.EXTRA_CPU_COUNT, CpuFeatures.getCount()); |
+ bundle.putLong(ChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.getMask()); |
+ bundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS, Linker.getInstance().getSharedRelros()); |
+ return bundle; |
+ } |
+ |
+ // Below are methods that will eventually be moved to a content delegate class. |
+ |
+ private void onBeforeConnectionAllocated(Bundle commonParameters) { |
+ // TODO(jcivelli): move createServiceBundle in there. |
+ } |
+ |
+ private IBinder getIBinderCallback() { |
+ return mIBinderCallback; |
+ } |
+ |
+ // Testing only related methods. |
+ @VisibleForTesting |
+ public static ChildProcessLauncherHelper createAndStartForTesting(long nativePointer, |
+ String[] commandLine, FileDescriptorInfo[] filesToBeMapped, |
+ ChildProcessCreationParams creationParams, boolean sandboxed, |
+ boolean alwaysInForeground) { |
+ String processType = |
+ ContentSwitches.getSwitchValue(commandLine, ContentSwitches.SWITCH_PROCESS_TYPE); |
+ ChildProcessLauncherHelper launcherHelper = |
+ new ChildProcessLauncherHelper(nativePointer, processType); |
+ launcherHelper.start(ContextUtils.getApplicationContext(), commandLine, filesToBeMapped, |
+ creationParams, sandboxed, alwaysInForeground); |
+ return launcherHelper; |
+ } |
+ |
+ @VisibleForTesting |
+ public ChildProcessConnection getChildProcessConnection() { |
+ return mChildProcessConnection; |
+ } |
} |