| Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
|
| deleted file mode 100644
|
| index fea34e25cd8c15a2e6b8895a177800190a4bae00..0000000000000000000000000000000000000000
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
|
| +++ /dev/null
|
| @@ -1,426 +0,0 @@
|
| -// Copyright 2013 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.ComponentName;
|
| -import android.content.Context;
|
| -import android.content.Intent;
|
| -import android.content.ServiceConnection;
|
| -import android.os.Bundle;
|
| -import android.os.IBinder;
|
| -import android.os.ParcelFileDescriptor;
|
| -import android.util.Log;
|
| -
|
| -import org.chromium.base.CpuFeatures;
|
| -import org.chromium.base.ThreadUtils;
|
| -import org.chromium.base.TraceEvent;
|
| -import org.chromium.content.app.ChildProcessService;
|
| -import org.chromium.content.app.Linker;
|
| -import org.chromium.content.app.LinkerParams;
|
| -import org.chromium.content.common.IChildProcessCallback;
|
| -import org.chromium.content.common.IChildProcessService;
|
| -
|
| -import java.io.IOException;
|
| -
|
| -/**
|
| - * Manages a connection between the browser activity and a child service.
|
| - */
|
| -public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| - private final Context mContext;
|
| - private final int mServiceNumber;
|
| - private final boolean mInSandbox;
|
| - private final ChildProcessConnection.DeathCallback mDeathCallback;
|
| - private final Class<? extends ChildProcessService> mServiceClass;
|
| -
|
| - // Synchronization: While most internal flow occurs on the UI thread, the public API
|
| - // (specifically start and stop) may be called from any thread, hence all entry point methods
|
| - // into the class are synchronized on the lock to protect access to these members. But see also
|
| - // the TODO where AsyncBoundServiceConnection is created.
|
| - private final Object mLock = new Object();
|
| - private IChildProcessService mService = null;
|
| - // Set to true when the service connect is finished, even if it fails.
|
| - private boolean mServiceConnectComplete = false;
|
| - // Set to true when the service disconnects, as opposed to being properly closed. This happens
|
| - // when the process crashes or gets killed by the system out-of-memory killer.
|
| - private boolean mServiceDisconnected = false;
|
| - // When the service disconnects (i.e. mServiceDisconnected is set to true), the status of the
|
| - // oom bindings is stashed here for future inspection.
|
| - private boolean mWasOomProtected = false;
|
| - private int mPID = 0; // Process ID of the corresponding child process.
|
| - // Initial binding protects the newly spawned process from being killed before it is put to use,
|
| - // it is maintained between calls to start() and removeInitialBinding().
|
| - private ChildServiceConnection mInitialBinding = null;
|
| - // Strong binding will make the service priority equal to the priority of the activity. We want
|
| - // the OS to be able to kill background renderers as it kills other background apps, so strong
|
| - // bindings are maintained only for services that are active at the moment (between
|
| - // addStrongBinding() and removeStrongBinding()).
|
| - private ChildServiceConnection mStrongBinding = null;
|
| - // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
|
| - // to start() and stop().
|
| - private ChildServiceConnection mWaivedBinding = null;
|
| - // Incremented on addStrongBinding(), decremented on removeStrongBinding().
|
| - private int mStrongBindingCount = 0;
|
| -
|
| - // Linker-related parameters.
|
| - private LinkerParams mLinkerParams = null;
|
| -
|
| - private static final String TAG = "ChildProcessConnection";
|
| -
|
| - private static class ConnectionParams {
|
| - final String[] mCommandLine;
|
| - final FileDescriptorInfo[] mFilesToBeMapped;
|
| - final IChildProcessCallback mCallback;
|
| - final Bundle mSharedRelros;
|
| -
|
| - ConnectionParams(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
|
| - IChildProcessCallback callback, Bundle sharedRelros) {
|
| - mCommandLine = commandLine;
|
| - mFilesToBeMapped = filesToBeMapped;
|
| - mCallback = callback;
|
| - mSharedRelros = sharedRelros;
|
| - }
|
| - }
|
| -
|
| - // This is set by the consumer of the class in setupConnection() and is later used in
|
| - // doSetupConnection(), after which the variable is cleared. Therefore this is only valid while
|
| - // the connection is being set up.
|
| - private ConnectionParams mConnectionParams;
|
| -
|
| - // Callbacks used to notify the consumer about connection events. This is also provided in
|
| - // setupConnection(), but remains valid after setup.
|
| - private ChildProcessConnection.ConnectionCallback mConnectionCallback;
|
| -
|
| - private class ChildServiceConnection implements ServiceConnection {
|
| - private boolean mBound = false;
|
| -
|
| - private final int mBindFlags;
|
| -
|
| - private Intent createServiceBindIntent() {
|
| - Intent intent = new Intent();
|
| - intent.setClassName(mContext, mServiceClass.getName() + mServiceNumber);
|
| - intent.setPackage(mContext.getPackageName());
|
| - return intent;
|
| - }
|
| -
|
| - public ChildServiceConnection(int bindFlags) {
|
| - mBindFlags = bindFlags;
|
| - }
|
| -
|
| - boolean bind(String[] commandLine) {
|
| - if (!mBound) {
|
| - final Intent intent = createServiceBindIntent();
|
| - if (commandLine != null) {
|
| - intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
|
| - }
|
| - if (mLinkerParams != null)
|
| - mLinkerParams.addIntentExtras(intent);
|
| - mBound = mContext.bindService(intent, this, mBindFlags);
|
| - }
|
| - return mBound;
|
| - }
|
| -
|
| - void unbind() {
|
| - if (mBound) {
|
| - mContext.unbindService(this);
|
| - mBound = false;
|
| - }
|
| - }
|
| -
|
| - boolean isBound() {
|
| - return mBound;
|
| - }
|
| -
|
| - @Override
|
| - public void onServiceConnected(ComponentName className, IBinder service) {
|
| - synchronized (mLock) {
|
| - // A flag from the parent class ensures we run the post-connection logic only once
|
| - // (instead of once per each ChildServiceConnection).
|
| - if (mServiceConnectComplete) {
|
| - return;
|
| - }
|
| - TraceEvent.begin();
|
| - mServiceConnectComplete = true;
|
| - mService = IChildProcessService.Stub.asInterface(service);
|
| - // Make sure that the connection parameters have already been provided. If not,
|
| - // doConnectionSetup() will be called from setupConnection().
|
| - if (mConnectionParams != null) {
|
| - doConnectionSetup();
|
| - }
|
| - TraceEvent.end();
|
| - }
|
| - }
|
| -
|
| -
|
| - // Called on the main thread to notify that the child service did not disconnect gracefully.
|
| - @Override
|
| - public void onServiceDisconnected(ComponentName className) {
|
| - synchronized (mLock) {
|
| - // Ensure that the disconnection logic runs only once (instead of once per each
|
| - // ChildServiceConnection).
|
| - if (mServiceDisconnected) {
|
| - return;
|
| - }
|
| - mServiceDisconnected = true;
|
| - // Stash the status of the oom bindings, since stop() will release all bindings.
|
| - mWasOomProtected = mInitialBinding.isBound() || mStrongBinding.isBound();
|
| - }
|
| - int pid = mPID; // Stash the pid for DeathCallback since stop() will clear it.
|
| - boolean disconnectedWhileBeingSetUp = mConnectionParams != null;
|
| - Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=" + pid);
|
| - stop(); // We don't want to auto-restart on crash. Let the browser do that.
|
| - if (pid != 0) {
|
| - mDeathCallback.onChildProcessDied(pid);
|
| - }
|
| - // TODO(ppi): does anyone know why we need to do that?
|
| - if (disconnectedWhileBeingSetUp && mConnectionCallback != null) {
|
| - mConnectionCallback.onConnected(0);
|
| - }
|
| - }
|
| - }
|
| -
|
| - ChildProcessConnectionImpl(Context context, int number, boolean inSandbox,
|
| - ChildProcessConnection.DeathCallback deathCallback,
|
| - Class<? extends ChildProcessService> serviceClass,
|
| - LinkerParams linkerParams) {
|
| - mContext = context;
|
| - mServiceNumber = number;
|
| - mInSandbox = inSandbox;
|
| - mDeathCallback = deathCallback;
|
| - mServiceClass = serviceClass;
|
| - mLinkerParams = linkerParams;
|
| - mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE);
|
| - mStrongBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
|
| - mWaivedBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
|
| - }
|
| -
|
| - @Override
|
| - public int getServiceNumber() {
|
| - return mServiceNumber;
|
| - }
|
| -
|
| - @Override
|
| - public boolean isInSandbox() {
|
| - return mInSandbox;
|
| - }
|
| -
|
| - @Override
|
| - public IChildProcessService getService() {
|
| - synchronized (mLock) {
|
| - return mService;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public int getPid() {
|
| - synchronized (mLock) {
|
| - return mPID;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void start(String[] commandLine) {
|
| - synchronized (mLock) {
|
| - TraceEvent.begin();
|
| - assert !ThreadUtils.runningOnUiThread();
|
| -
|
| - if (!mInitialBinding.bind(commandLine)) {
|
| - onBindFailed();
|
| - } else {
|
| - mWaivedBinding.bind(null);
|
| - }
|
| - TraceEvent.end();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void setupConnection(
|
| - String[] commandLine,
|
| - FileDescriptorInfo[] filesToBeMapped,
|
| - IChildProcessCallback processCallback,
|
| - ConnectionCallback connectionCallbacks,
|
| - Bundle sharedRelros) {
|
| - synchronized (mLock) {
|
| - TraceEvent.begin();
|
| - assert mConnectionParams == null;
|
| - mConnectionCallback = connectionCallbacks;
|
| - mConnectionParams = new ConnectionParams(
|
| - commandLine, filesToBeMapped, processCallback, sharedRelros);
|
| - // Make sure that the service is already connected. If not, doConnectionSetup() will be
|
| - // called from onServiceConnected().
|
| - if (mServiceConnectComplete) {
|
| - doConnectionSetup();
|
| - }
|
| - TraceEvent.end();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void stop() {
|
| - synchronized (mLock) {
|
| - mInitialBinding.unbind();
|
| - mStrongBinding.unbind();
|
| - mWaivedBinding.unbind();
|
| - mStrongBindingCount = 0;
|
| - if (mService != null) {
|
| - mService = null;
|
| - mPID = 0;
|
| - }
|
| - mConnectionParams = null;
|
| - mServiceConnectComplete = false;
|
| - }
|
| - }
|
| -
|
| - // Called on the main thread to notify that the bindService() call failed (returned false).
|
| - private void onBindFailed() {
|
| - mServiceConnectComplete = true;
|
| - if (mConnectionParams != null) {
|
| - doConnectionSetup();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Called after the connection parameters have been set (in setupConnection()) *and* a
|
| - * connection has been established (as signaled by onServiceConnected()) or failed (as signaled
|
| - * by onBindFailed(), in this case mService will be null). These two events can happen in any
|
| - * order.
|
| - */
|
| - private void doConnectionSetup() {
|
| - TraceEvent.begin();
|
| - assert mServiceConnectComplete && mConnectionParams != null;
|
| -
|
| - if (mService != null) {
|
| - Bundle bundle = new Bundle();
|
| - bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
|
| -
|
| - FileDescriptorInfo[] fileInfos = mConnectionParams.mFilesToBeMapped;
|
| - ParcelFileDescriptor[] parcelFiles = new ParcelFileDescriptor[fileInfos.length];
|
| - for (int i = 0; i < fileInfos.length; i++) {
|
| - if (fileInfos[i].mFd == -1) {
|
| - // If someone provided an invalid FD, they are doing something wrong.
|
| - Log.e(TAG, "Invalid FD (id=" + fileInfos[i].mId + ") for process connection, "
|
| - + "aborting connection.");
|
| - return;
|
| - }
|
| - String idName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_ID_SUFFIX;
|
| - String fdName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_FD_SUFFIX;
|
| - if (fileInfos[i].mAutoClose) {
|
| - // Adopt the FD, it will be closed when we close the ParcelFileDescriptor.
|
| - parcelFiles[i] = ParcelFileDescriptor.adoptFd(fileInfos[i].mFd);
|
| - } else {
|
| - try {
|
| - parcelFiles[i] = ParcelFileDescriptor.fromFd(fileInfos[i].mFd);
|
| - } catch (IOException e) {
|
| - Log.e(TAG,
|
| - "Invalid FD provided for process connection, aborting connection.",
|
| - e);
|
| - return;
|
| - }
|
| -
|
| - }
|
| - bundle.putParcelable(fdName, parcelFiles[i]);
|
| - bundle.putInt(idName, fileInfos[i].mId);
|
| - }
|
| - // Add the CPU properties now.
|
| - bundle.putInt(EXTRA_CPU_COUNT, CpuFeatures.getCount());
|
| - bundle.putLong(EXTRA_CPU_FEATURES, CpuFeatures.getMask());
|
| -
|
| - bundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS,
|
| - mConnectionParams.mSharedRelros);
|
| -
|
| - try {
|
| - mPID = mService.setupConnection(bundle, mConnectionParams.mCallback);
|
| - } catch (android.os.RemoteException re) {
|
| - Log.e(TAG, "Failed to setup connection.", re);
|
| - }
|
| - // We proactively close the FDs rather than wait for GC & finalizer.
|
| - try {
|
| - for (ParcelFileDescriptor parcelFile : parcelFiles) {
|
| - if (parcelFile != null) parcelFile.close();
|
| - }
|
| - } catch (IOException ioe) {
|
| - Log.w(TAG, "Failed to close FD.", ioe);
|
| - }
|
| - }
|
| - mConnectionParams = null;
|
| -
|
| - if (mConnectionCallback != null) {
|
| - mConnectionCallback.onConnected(getPid());
|
| - }
|
| - TraceEvent.end();
|
| - }
|
| -
|
| - @Override
|
| - public boolean isInitialBindingBound() {
|
| - synchronized (mLock) {
|
| - return mInitialBinding.isBound();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isStrongBindingBound() {
|
| - synchronized (mLock) {
|
| - return mStrongBinding.isBound();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void removeInitialBinding() {
|
| - synchronized (mLock) {
|
| - mInitialBinding.unbind();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isOomProtectedOrWasWhenDied() {
|
| - synchronized (mLock) {
|
| - if (mServiceDisconnected) {
|
| - return mWasOomProtected;
|
| - } else {
|
| - return mInitialBinding.isBound() || mStrongBinding.isBound();
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void dropOomBindings() {
|
| - synchronized (mLock) {
|
| - mInitialBinding.unbind();
|
| -
|
| - mStrongBindingCount = 0;
|
| - mStrongBinding.unbind();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void addStrongBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for " + mPID);
|
| - return;
|
| - }
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.bind(null);
|
| - }
|
| - mStrongBindingCount++;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void removeStrongBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for " + mPID);
|
| - return;
|
| - }
|
| - assert mStrongBindingCount > 0;
|
| - mStrongBindingCount--;
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.unbind();
|
| - }
|
| - }
|
| - }
|
| -}
|
|
|