| Index: content/public/android/java/org/chromium/content/browser/SandboxedProcessLauncher.java
|
| diff --git a/content/public/android/java/org/chromium/content/browser/SandboxedProcessLauncher.java b/content/public/android/java/org/chromium/content/browser/SandboxedProcessLauncher.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4dd9f1f40e416643f65995f253f007f29d5ab327
|
| --- /dev/null
|
| +++ b/content/public/android/java/org/chromium/content/browser/SandboxedProcessLauncher.java
|
| @@ -0,0 +1,305 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.content.browser;
|
| +
|
| +import android.content.Context;
|
| +import android.os.RemoteException;
|
| +import android.util.Log;
|
| +import android.view.Surface;
|
| +
|
| +import java.util.Map;
|
| +import java.util.concurrent.ConcurrentHashMap;
|
| +
|
| +import org.chromium.base.CalledByNative;
|
| +import org.chromium.content.browser.CommandLine;
|
| +import org.chromium.content.common.ISandboxedProcessCallback;
|
| +import org.chromium.content.common.ISandboxedProcessService;
|
| +
|
| +/**
|
| + * This class provides the method to start/stop SandboxedProcess called by
|
| + * native.
|
| + */
|
| +public class SandboxedProcessLauncher {
|
| + private static String TAG = "SandboxedProcessLauncher";
|
| +
|
| + // The upper limit on the number of simultaneous service process instances supported.
|
| + // This must not exceed total number of SandboxedProcessServiceX classes declared in
|
| + // this package, and defined as services in the embedding application's manifest file.
|
| + // (See {@link SandboxedProcessService} for more details on defining the services.)
|
| + /* package */ static final int MAX_REGISTERED_SERVICES = 5;
|
| + private static final SandboxedProcessConnection[] mConnections =
|
| + new SandboxedProcessConnection[MAX_REGISTERED_SERVICES];
|
| +
|
| + private static int sNumRegisteredServices = -1;
|
| +
|
| + private static int getNumRegisteredServices() {
|
| + if (sNumRegisteredServices < 0) {
|
| + String s =
|
| + CommandLine.getInstance().getSwitchValue(CommandLine.SANDBOXED_SERVICE_LIMIT);
|
| + sNumRegisteredServices = MAX_REGISTERED_SERVICES;
|
| + if (s != null) {
|
| + try {
|
| + sNumRegisteredServices = Integer.parseInt(s);
|
| + } catch (java.lang.NumberFormatException e) {
|
| + // pass
|
| + }
|
| + }
|
| + }
|
| + return sNumRegisteredServices;
|
| + }
|
| +
|
| + private static SandboxedProcessConnection allocateConnection(Context context) {
|
| + SandboxedProcessConnection.DeathCallback deathCallback =
|
| + new SandboxedProcessConnection.DeathCallback() {
|
| + @Override
|
| + public void onSandboxedProcessDied(int pid) {
|
| + stop(pid);
|
| + }
|
| + };
|
| + synchronized (mConnections) {
|
| + for (int i = 0; i < getNumRegisteredServices(); ++i) {
|
| + if (mConnections[i] == null) {
|
| + mConnections[i] = new SandboxedProcessConnection(context, i, deathCallback);
|
| + return mConnections[i];
|
| + }
|
| + }
|
| + }
|
| + Log.w(TAG, "Ran out of sandboxed services.");
|
| + return null;
|
| + }
|
| +
|
| + private static SandboxedProcessConnection allocateBoundConnection(Context context,
|
| + String[] commandLine) {
|
| + SandboxedProcessConnection connection = allocateConnection(context);
|
| + if (connection != null) {
|
| + connection.bind(commandLine);
|
| + }
|
| + return connection;
|
| + }
|
| +
|
| + private static void freeConnection(SandboxedProcessConnection connection) {
|
| + if (connection == null) {
|
| + return;
|
| + }
|
| + int slot = connection.getServiceNumber();
|
| + synchronized (mConnections) {
|
| + if (mConnections[slot] != connection) {
|
| + int occupier = mConnections[slot] == null ?
|
| + -1 : mConnections[slot].getServiceNumber();
|
| + Log.e(TAG, "Unable to find connection to free in slot: " + slot +
|
| + " already occupied by service: " + occupier);
|
| + assert false;
|
| + } else {
|
| + mConnections[slot] = null;
|
| + }
|
| + }
|
| + }
|
| +
|
| + public static int getNumberOfConnections() {
|
| + int result = 0;
|
| + synchronized (mConnections) {
|
| + for (int i = 0; i < getNumRegisteredServices(); ++i) {
|
| + if (mConnections[i] != null)
|
| + ++result;
|
| + }
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + // Represents an invalid process handle; same as base/process.h kNullProcessHandle.
|
| + private static final int NULL_PROCESS_HANDLE = 0;
|
| +
|
| + // Map from pid to SandboxedService connection.
|
| + private static Map<Integer, SandboxedProcessConnection> mServiceMap =
|
| + new ConcurrentHashMap<Integer, SandboxedProcessConnection>();
|
| +
|
| + // A pre-allocated and pre-bound connection ready for connection setup, or null.
|
| + static SandboxedProcessConnection mSpareConnection = null;
|
| +
|
| + /**
|
| + * Returns the sandboxed process service interface for the given pid. This may be called on
|
| + * any thread, but the caller must assume that the service can disconnect at any time. All
|
| + * service calls should catch and handle android.os.RemoteException.
|
| + *
|
| + * @param pid The pid (process handle) of the service obtained from {@link #start}.
|
| + * @return The ISandboxedProcessService or null if the service no longer exists.
|
| + */
|
| + public static ISandboxedProcessService getSandboxedService(int pid) {
|
| + SandboxedProcessConnection connection = mServiceMap.get(pid);
|
| + if (connection != null) {
|
| + return connection.getService();
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * Should be called early in startup so the work needed to spawn the sandboxed process can
|
| + * be done in parallel to other startup work.
|
| + * @param context the application context used for the connection.
|
| + */
|
| + public static synchronized void warmUp(Context context) {
|
| + if (mSpareConnection == null) {
|
| + mSpareConnection = allocateBoundConnection(context, null);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Spawns and connects to a sandboxed process. May be called on any thread. It will not
|
| + * block, but will instead callback to {@link #nativeOnSandboxedProcessStarted} when the
|
| + * connection is established. Note this callback will not necessarily be from the same thread
|
| + * (currently it always comes from the main thread).
|
| + *
|
| + * @param context Context used to obtain the application context.
|
| + * @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(
|
| + Context context,
|
| + final String[] commandLine,
|
| + int ipcFd,
|
| + int crashFd,
|
| + final int clientContext) {
|
| + assert clientContext != 0;
|
| + SandboxedProcessConnection allocatedConnection;
|
| + synchronized (SandboxedProcessLauncher.class) {
|
| + allocatedConnection = mSpareConnection;
|
| + mSpareConnection = null;
|
| + }
|
| + if (allocatedConnection == null) {
|
| + allocatedConnection = allocateBoundConnection(context, commandLine);
|
| + if (allocatedConnection == null) {
|
| + return null;
|
| + }
|
| + }
|
| + final SandboxedProcessConnection connection = allocatedConnection;
|
| + Log.d(TAG, "Setting up connection to process: slot=" + connection.getServiceNumber());
|
| + // Note: This runnable will be executed when the sandboxed connection is setup.
|
| + final Runnable onConnect = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + final int pid = connection.getPid();
|
| + Log.d(TAG, "on connect callback, pid=" + pid + " context=" + clientContext);
|
| + if (pid != NULL_PROCESS_HANDLE) {
|
| + mServiceMap.put(pid, connection);
|
| + } else {
|
| + freeConnection(connection);
|
| + }
|
| + 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);
|
| + }
|
| +
|
| + /**
|
| + * Terminates a sandboxed process. This may be called from any thread.
|
| + *
|
| + * @param pid The pid (process handle) of the service connection obtained from {@link #start}.
|
| + */
|
| + @CalledByNative
|
| + static void stop(int pid) {
|
| + Log.d(TAG, "stopping sandboxed connection: pid=" + pid);
|
| +
|
| + SandboxedProcessConnection connection = mServiceMap.remove(pid);
|
| + if (connection == null) {
|
| + Log.w(TAG, "Tried to stop non-existent connection to pid: " + pid);
|
| + return;
|
| + }
|
| + connection.unbind();
|
| + freeConnection(connection);
|
| + }
|
| +
|
| + /**
|
| + * Bind a sandboxed process as a high priority process so that it has the same
|
| + * priority as the main process. This can be used for the foreground renderer
|
| + * process to distinguish it from the the background renderer process.
|
| + *
|
| + * @param pid The process handle of the service connection obtained from {@link #start}.
|
| + */
|
| + static void bindAsHighPriority(int pid) {
|
| + SandboxedProcessConnection connection = mServiceMap.get(pid);
|
| + if (connection == null) {
|
| + Log.w(TAG, "Tried to bind a non-existent connection to pid: " + pid);
|
| + return;
|
| + }
|
| + connection.bindHighPriority();
|
| + }
|
| +
|
| + /**
|
| + * Unbind a high priority process which is bound by {@link #bindAsHighPriority}.
|
| + *
|
| + * @param pid The process handle of the service obtained from {@link #start}.
|
| + */
|
| + static void unbindAsHighPriority(int pid) {
|
| + SandboxedProcessConnection connection = mServiceMap.get(pid);
|
| + if (connection == null) {
|
| + Log.w(TAG, "Tried to unbind non-existent connection to pid: " + pid);
|
| + return;
|
| + }
|
| + connection.unbindHighPriority(false);
|
| + }
|
| +
|
| + static void establishSurfacePeer(
|
| + int pid, int type, Surface surface, int primaryID, int secondaryID) {
|
| + Log.d(TAG, "establishSurfaceTexturePeer: pid = " + pid + ", " +
|
| + "type = " + type + ", " +
|
| + "primaryID = " + primaryID + ", " +
|
| + "secondaryID = " + secondaryID);
|
| + ISandboxedProcessService service = SandboxedProcessLauncher.getSandboxedService(pid);
|
| + if (service == null) {
|
| + Log.e(TAG, "Unable to get SandboxedProcessService from pid.");
|
| + return;
|
| + }
|
| + try {
|
| + service.setSurface(type, surface, primaryID, secondaryID);
|
| + } catch (RemoteException e) {
|
| + Log.e(TAG, "Unable to call setSurface: " + e);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * This implementation is used to receive callbacks from the remote service.
|
| + */
|
| + private static ISandboxedProcessCallback createCallback() {
|
| + return new ISandboxedProcessCallback.Stub() {
|
| + /**
|
| + * This is called by the remote service regularly to tell us about
|
| + * new values. Note that IPC calls are dispatched through a thread
|
| + * pool running in each process, so the code executing here will
|
| + * NOT be running in our main thread -- so, to update the UI, we need
|
| + * to use a Handler.
|
| + */
|
| + public void establishSurfacePeer(
|
| + int pid, int type, Surface surface, int primaryID, int secondaryID) {
|
| + SandboxedProcessLauncher.establishSurfacePeer(pid, type, surface,
|
| + primaryID, secondaryID);
|
| + // The SandboxProcessService now holds a reference to the
|
| + // Surface's resources, so we release our reference to it now to
|
| + // avoid waiting for the finalizer to get around to it.
|
| + if (surface != null) {
|
| + surface.release();
|
| + }
|
| + }
|
| + };
|
| + };
|
| +
|
| + private static native void nativeOnSandboxedProcessStarted(int clientContext, int pid);
|
| +}
|
|
|