| Index: content/public/android/java/org/chromium/content/app/SandboxedProcessService.java
|
| diff --git a/content/public/android/java/org/chromium/content/app/SandboxedProcessService.java b/content/public/android/java/org/chromium/content/app/SandboxedProcessService.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fe052ccf7024bb484f17502d2cfe2b7d00f8a7c3
|
| --- /dev/null
|
| +++ b/content/public/android/java/org/chromium/content/app/SandboxedProcessService.java
|
| @@ -0,0 +1,227 @@
|
| +// 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.app;
|
| +
|
| +import android.app.Service;
|
| +import android.content.Context;
|
| +import android.content.Intent;
|
| +import android.graphics.SurfaceTexture;
|
| +import android.os.Bundle;
|
| +import android.os.IBinder;
|
| +import android.os.ParcelFileDescriptor;
|
| +import android.os.Process;
|
| +import android.os.RemoteException;
|
| +import android.util.Log;
|
| +import android.view.Surface;
|
| +
|
| +import org.chromium.base.CalledByNative;
|
| +import org.chromium.content.app.ContentMain;
|
| +import org.chromium.content.browser.SandboxedProcessConnection;
|
| +import org.chromium.content.common.ISandboxedProcessCallback;
|
| +import org.chromium.content.common.ISandboxedProcessService;
|
| +import org.chromium.content.common.SurfaceCallback;
|
| +
|
| +/**
|
| + * This is the base class for sandboxed services; the SandboxedProcessService0, 1.. etc
|
| + * subclasses provide the concrete service entry points, to enable the browser to connect
|
| + * to more than one distinct process (i.e. one process per service number, up to limit of N).
|
| + * The embedding application must declare these service instances in the application section
|
| + * of its AndroidManifest.xml, for example with N entries of the form:-
|
| + * <service android:name="org.chromium.content.browser.SandboxedProcessServiceX"
|
| + * android:process=":sandboxed_processX" />
|
| + * for X in 0...N-1 (where N is {@link SandboxedProcessLauncher#MAX_REGISTERED_SERVICES})
|
| + */
|
| +public class SandboxedProcessService extends Service {
|
| + private static final String MAIN_THREAD_NAME = "SandboxedProcessMain";
|
| + private static final String TAG = "SandboxedProcessService";
|
| + private ISandboxedProcessCallback mCallback;
|
| +
|
| + // 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[] mCommandLineParams;
|
| + private ParcelFileDescriptor mIPCFd;
|
| + private ParcelFileDescriptor mCrashFd;
|
| +
|
| + private static Context sContext = null;
|
| + private boolean mLibraryInitialized = false;
|
| +
|
| + // Binder object used by clients for this service.
|
| + private final ISandboxedProcessService.Stub mBinder = new ISandboxedProcessService.Stub() {
|
| + // NOTE: Implement any ISandboxedProcessService methods here.
|
| + @Override
|
| + public int setupConnection(Bundle args, ISandboxedProcessCallback callback) {
|
| + mCallback = callback;
|
| + synchronized (mSandboxMainThread) {
|
| + // Allow the command line to be set via bind() intent or setupConnection, but
|
| + // the FD can only be transferred here.
|
| + if (mCommandLineParams == null) {
|
| + mCommandLineParams = args.getStringArray(
|
| + SandboxedProcessConnection.EXTRA_COMMAND_LINE);
|
| + }
|
| + // 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);
|
| + }
|
| + mSandboxMainThread.notifyAll();
|
| + }
|
| + return Process.myPid();
|
| + }
|
| +
|
| + @Override
|
| + public void setSurface(int type, Surface surface, int primaryID, int secondaryID) {
|
| + // This gives up ownership of the Surface.
|
| + SurfaceCallback.setSurface(type, surface, primaryID, secondaryID);
|
| + }
|
| + };
|
| +
|
| + /* package */ static Context getContext() {
|
| + return sContext;
|
| + }
|
| +
|
| + @Override
|
| + public void onCreate() {
|
| + sContext = this;
|
| + super.onCreate();
|
| +
|
| + mSandboxMainThread = new Thread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + try {
|
| + // TODO(michaelbai): Upstream LibraryLoader.java
|
| + // if (!LibraryLoader.loadNow()) return;
|
| + synchronized (mSandboxMainThread) {
|
| + while (mCommandLineParams == null) {
|
| + mSandboxMainThread.wait();
|
| + }
|
| + }
|
| + // LibraryLoader.initializeOnMainThread(mCommandLineParams);
|
| + synchronized (mSandboxMainThread) {
|
| + mLibraryInitialized = true;
|
| + mSandboxMainThread.notifyAll();
|
| + while (mIPCFd == null) {
|
| + mSandboxMainThread.wait();
|
| + }
|
| + }
|
| + int crashFd = (mCrashFd == null) ? -1 : mCrashFd.detachFd();
|
| + ContentMain.initApplicationContext(sContext.getApplicationContext());
|
| + nativeInitSandboxedProcess(sContext.getApplicationContext(),
|
| + SandboxedProcessService.this, mIPCFd.detachFd(), crashFd);
|
| + ContentMain.start();
|
| + nativeExitSandboxedProcess();
|
| + } catch (InterruptedException e) {
|
| + Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e);
|
| + }
|
| + }
|
| + }, MAIN_THREAD_NAME);
|
| + mSandboxMainThread.start();
|
| + }
|
| +
|
| + @Override
|
| + public void onDestroy() {
|
| + super.onDestroy();
|
| + if (mCommandLineParams == null) {
|
| + // This process was destroyed before it even started. Nothing more to do.
|
| + return;
|
| + }
|
| + synchronized (mSandboxMainThread) {
|
| + try {
|
| + while (!mLibraryInitialized) {
|
| + // Avoid a potential race in calling through to native code before the library
|
| + // has loaded.
|
| + mSandboxMainThread.wait();
|
| + }
|
| + } catch (InterruptedException e) {
|
| + }
|
| + }
|
| +
|
| + // This is not synchronized with the main thread in any way, but this is analogous
|
| + // to how desktop chrome terminates processes using SIGTERM. The mSandboxMainThread
|
| + // may run briefly before this is executed, but will eventually get a channel error
|
| + // and similarly commit suicide via SuicideOnChannelErrorFilter().
|
| + // TODO(tedbo): Why doesn't the activity manager SIGTERM/SIGKILL this service process?
|
| + nativeExitSandboxedProcess();
|
| + }
|
| +
|
| + @Override
|
| + public IBinder onBind(Intent intent) {
|
| + // We call stopSelf() to request that this service be stopped as soon as the client
|
| + // unbinds. Otherwise the system may keep it around and available for a reconnect. The
|
| + // sandboxed processes do not currently support reconnect; they must be initialized from
|
| + // scratch every time.
|
| + stopSelf();
|
| +
|
| + synchronized (mSandboxMainThread) {
|
| + mCommandLineParams = intent.getStringArrayExtra(
|
| + SandboxedProcessConnection.EXTRA_COMMAND_LINE);
|
| + mSandboxMainThread.notifyAll();
|
| + }
|
| +
|
| + return mBinder;
|
| + }
|
| +
|
| + /**
|
| + * Called from native code to share a surface texture with another child process.
|
| + * Through using the callback object the browser is used as a proxy to route the
|
| + * call to the correct process.
|
| + *
|
| + * @param pid Process handle of the sandboxed process to share the SurfaceTexture with.
|
| + * @param type The type of process that the SurfaceTexture is for.
|
| + * @param surfaceObject The Surface or SurfaceTexture to share with the other sandboxed process.
|
| + * @param primaryID Used to route the call to the correct client instance.
|
| + * @param secondaryID Used to route the call to the correct client instance.
|
| + */
|
| + @SuppressWarnings("unused")
|
| + @CalledByNative
|
| + 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;
|
| + }
|
| +
|
| + Surface surface = null;
|
| + boolean needRelease = false;
|
| + if (surfaceObject instanceof Surface) {
|
| + surface = (Surface)surfaceObject;
|
| + } else if (surfaceObject instanceof SurfaceTexture) {
|
| + surface = new Surface((SurfaceTexture)surfaceObject);
|
| + needRelease = true;
|
| + } else {
|
| + Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject);
|
| + return;
|
| + }
|
| + try {
|
| + mCallback.establishSurfacePeer(pid, type, surface, primaryID, secondaryID);
|
| + } catch (RemoteException e) {
|
| + Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
|
| + return;
|
| + } finally {
|
| + if (needRelease) {
|
| + surface.release();
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * The main entry point for a sandboxed process. This should be called from a new thread since
|
| + * it will not return until the sandboxed process exits. See sandboxed_process_service.{h,cc}
|
| + *
|
| + * @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.
|
| + */
|
| + private static native void nativeInitSandboxedProcess(Context applicationContext,
|
| + SandboxedProcessService service, int ipcFd, int crashFd);
|
| +
|
| + /**
|
| + * Force the sandboxed process to exit.
|
| + */
|
| + private static native void nativeExitSandboxedProcess();
|
| +}
|
|
|