| Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
|
| index eb1a5cd60877879e1e57507f86be295d153344f8..682ce08a30ef968965be50309c74ddb57f5f2c9d 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
|
| @@ -34,10 +34,42 @@ import org.chromium.content.common.TraceEvent;
|
| * the service when it is in active use (between calls to attachAsActive() and detachAsActive()).
|
| */
|
| public class ChildProcessConnection {
|
| + /**
|
| + * Used to notify the consumer about disconnection of the service. This callback is provided
|
| + * earlier than ConnectionCallbacks below, as a child process might die before the connection is
|
| + * fully set up.
|
| + */
|
| interface DeathCallback {
|
| void onChildProcessDied(int pid);
|
| }
|
|
|
| + /**
|
| + * Used to notify the consumer about the connection being established and about out-of-memory
|
| + * bindings being bound for the connection. "Out-of-memory" bindings are bindings that raise the
|
| + * priority of the service process so that it does not get killed by the OS out-of-memory killer
|
| + * during normal operation (yet it still may get killed under drastic memory pressure).
|
| + */
|
| + interface ConnectionCallbacks {
|
| + /**
|
| + * Called when the connection to the service is established. It will be called before any
|
| + * calls to onOomBindingsAdded(), onOomBindingRemoved().
|
| + * @param pid Pid of the child process.
|
| + * @param oomBindingCount Number of the out-of-memory bindings bound before the connection
|
| + * was established.
|
| + */
|
| + void onConnected(int pid, int oomBindingCount);
|
| +
|
| + /**
|
| + * Called when a new out-of-memory binding is bound.
|
| + */
|
| + void onOomBindingAdded(int pid);
|
| +
|
| + /**
|
| + * Called when an out-of-memory binding is unbound.
|
| + */
|
| + void onOomBindingRemoved(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.command_line";
|
| @@ -68,6 +100,9 @@ public class ChildProcessConnection {
|
| 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;
|
| 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().
|
| @@ -89,17 +124,12 @@ public class ChildProcessConnection {
|
| final String[] mCommandLine;
|
| final FileDescriptorInfo[] mFilesToBeMapped;
|
| final IChildProcessCallback mCallback;
|
| - final Runnable mOnConnectionCallback;
|
|
|
| - ConnectionParams(
|
| - String[] commandLine,
|
| - FileDescriptorInfo[] filesToBeMapped,
|
| - IChildProcessCallback callback,
|
| - Runnable onConnectionCallback) {
|
| + ConnectionParams(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
|
| + IChildProcessCallback callback) {
|
| mCommandLine = commandLine;
|
| mFilesToBeMapped = filesToBeMapped;
|
| mCallback = callback;
|
| - mOnConnectionCallback = onConnectionCallback;
|
| }
|
| }
|
|
|
| @@ -108,13 +138,19 @@ public class ChildProcessConnection {
|
| // 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.ConnectionCallbacks mConnectionCallbacks;
|
| +
|
| private class ChildServiceConnection implements ServiceConnection {
|
| private boolean mBound = false;
|
|
|
| private final int mBindFlags;
|
| + private final boolean mProtectsFromOom;
|
|
|
| - public ChildServiceConnection(int bindFlags) {
|
| + public ChildServiceConnection(int bindFlags, boolean protectsFromOom) {
|
| mBindFlags = bindFlags;
|
| + mProtectsFromOom = protectsFromOom;
|
| }
|
|
|
| boolean bind(String[] commandLine) {
|
| @@ -124,6 +160,9 @@ public class ChildProcessConnection {
|
| intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
|
| }
|
| mBound = mContext.bindService(intent, this, mBindFlags);
|
| + if (mBound && mProtectsFromOom && mConnectionCallbacks != null) {
|
| + mConnectionCallbacks.onOomBindingAdded(getPid());
|
| + }
|
| }
|
| return mBound;
|
| }
|
| @@ -132,6 +171,12 @@ public class ChildProcessConnection {
|
| if (mBound) {
|
| mContext.unbindService(this);
|
| mBound = false;
|
| + // When the process crashes, we stop reporting bindings being unbound (so that their
|
| + // numbers can be inspected to determine if the process crash could be caused by the
|
| + // out-of-memory killing), hence the mServiceDisconnected check below.
|
| + if (mProtectsFromOom && mConnectionCallbacks != null && !mServiceDisconnected) {
|
| + mConnectionCallbacks.onOomBindingRemoved(getPid());
|
| + }
|
| }
|
| }
|
|
|
| @@ -150,6 +195,8 @@ public class ChildProcessConnection {
|
| 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();
|
| }
|
| @@ -163,19 +210,20 @@ public class ChildProcessConnection {
|
| public void onServiceDisconnected(ComponentName className) {
|
| // Ensure that the disconnection logic runs only once (instead of once per each
|
| // ChildServiceConnection).
|
| - if (mService == null) {
|
| + if (mServiceDisconnected) {
|
| return;
|
| }
|
| - int pid = mPID; // Stash pid & connection callback since stop() will clear them.
|
| - Runnable onConnectionCallback =
|
| - mConnectionParams != null ? mConnectionParams.mOnConnectionCallback : null;
|
| + mServiceDisconnected = true;
|
| + 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);
|
| }
|
| - if (onConnectionCallback != null) {
|
| - onConnectionCallback.run();
|
| + // TODO(ppi): does anyone know why we need to do that?
|
| + if (disconnectedWhileBeingSetUp && mConnectionCallbacks != null) {
|
| + mConnectionCallbacks.onConnected(0, 0);
|
| }
|
| }
|
| }
|
| @@ -188,11 +236,11 @@ public class ChildProcessConnection {
|
| mInSandbox = inSandbox;
|
| mDeathCallback = deathCallback;
|
| mServiceClass = serviceClass;
|
| - mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE);
|
| + mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE, true);
|
| mStrongBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
|
| + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, true);
|
| mWaivedBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
|
| + Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, false);
|
| }
|
|
|
| int getServiceNumber() {
|
| @@ -244,18 +292,21 @@ public class ChildProcessConnection {
|
| * @param commandLine (Optional) will be ignored if the command line was already sent in bind()
|
| * @param fileToBeMapped a list of file descriptors 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.
|
| + * @param connectionCallbacks will notify the consumer about the connection being established
|
| + * and the status of the out-of-memory bindings being bound for the connection.
|
| */
|
| void setupConnection(
|
| String[] commandLine,
|
| FileDescriptorInfo[] filesToBeMapped,
|
| - IChildProcessCallback callback,
|
| - Runnable onConnectionCallback) {
|
| + IChildProcessCallback processCallback,
|
| + ConnectionCallbacks connectionCallbacks) {
|
| synchronized(mUiThreadLock) {
|
| TraceEvent.begin();
|
| assert mConnectionParams == null;
|
| - mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, callback,
|
| - onConnectionCallback);
|
| + mConnectionCallbacks = connectionCallbacks;
|
| + mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, processCallback);
|
| + // Make sure that the service is already connected. If not, doConnectionSetup() will be
|
| + // called from onServiceConnected().
|
| if (mServiceConnectComplete) {
|
| doConnectionSetup();
|
| }
|
| @@ -291,18 +342,16 @@ public class ChildProcessConnection {
|
| }
|
|
|
| /**
|
| - * Called when the connection parameters have been set, and a connection has been established
|
| - * (as signaled by onServiceConnected), or if the connection failed (as signaled by
|
| - * onBindFailed), in which case mService will be false.
|
| + * 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;
|
| - // Capture the callback before it is potentially nulled in unbind().
|
| - Runnable onConnectionCallback = mConnectionParams.mOnConnectionCallback;
|
| - if (onConnectionCallback == null) {
|
| - stop();
|
| - } else if (mService != null) {
|
| +
|
| + if (mService != null) {
|
| Bundle bundle = new Bundle();
|
| bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
|
|
|
| @@ -343,7 +392,7 @@ public class ChildProcessConnection {
|
| } catch (android.os.RemoteException re) {
|
| Log.e(TAG, "Failed to setup connection.", re);
|
| }
|
| - // We proactivley close the FDs rather than wait for GC & finalizer.
|
| + // We proactively close the FDs rather than wait for GC & finalizer.
|
| try {
|
| for (ParcelFileDescriptor parcelFile : parcelFiles) {
|
| if (parcelFile != null) parcelFile.close();
|
| @@ -353,8 +402,12 @@ public class ChildProcessConnection {
|
| }
|
| }
|
| mConnectionParams = null;
|
| - if (onConnectionCallback != null) {
|
| - onConnectionCallback.run();
|
| +
|
| + if (mConnectionCallbacks != null) {
|
| + // Number of out-of-memory bindings bound before the connection was set up.
|
| + int oomBindingCount =
|
| + (mInitialBinding.isBound() ? 1 : 0) + (mStrongBinding.isBound() ? 1 : 0);
|
| + mConnectionCallbacks.onConnected(getPid(), oomBindingCount);
|
| }
|
| TraceEvent.end();
|
| }
|
| @@ -431,7 +484,7 @@ public class ChildProcessConnection {
|
| /**
|
| * @return The connection PID, or 0 if not yet connected.
|
| */
|
| - public int getPid() {
|
| + int getPid() {
|
| synchronized(mUiThreadLock) {
|
| return mPID;
|
| }
|
|
|