Index: content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..99f41223869821f5ca8c04537e8084c2ec9336f6 |
--- /dev/null |
+++ b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java |
@@ -0,0 +1,225 @@ |
+// 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.Context; |
+import android.os.Bundle; |
+ |
+import org.chromium.base.Log; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.process_launcher.ChildProcessCreationParams; |
+ |
+import javax.annotation.concurrent.GuardedBy; |
+ |
+/** |
+ * ManagedChildProcessConnection is a connection to a child service that can hold several bindings |
+ * to the service so it can be more or less agressively protected against OOM. |
+ */ |
+public class ManagedChildProcessConnection extends BaseChildProcessConnection { |
+ private static final String TAG = "ManChildProcessConn"; |
+ |
+ public static final Factory FACTORY = new BaseChildProcessConnection.Factory() { |
+ @Override |
+ public BaseChildProcessConnection create(Context context, int number, boolean sandboxed, |
+ DeathCallback deathCallback, String serviceClassName, |
+ Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) { |
+ return new ManagedChildProcessConnection(context, number, sandboxed, deathCallback, |
+ serviceClassName, childProcessCommonParameters, creationParams); |
+ } |
+ }; |
+ |
+ // 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. |
+ private final Object mBindingLock = new Object(); |
+ |
+ // 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(). |
+ @GuardedBy("mBindingLock") |
+ private final ChildServiceConnection mInitialBinding; |
+ |
+ // 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()). |
+ @GuardedBy("mBindingLock") |
+ private final ChildServiceConnection mStrongBinding; |
+ |
+ // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls |
+ // to start() and stop(). |
+ @GuardedBy("mBindingLock") |
+ private final ChildServiceConnection mWaivedBinding; |
+ |
+ // Incremented on addStrongBinding(), decremented on removeStrongBinding(). |
+ @GuardedBy("mBindingLock") |
+ private int mStrongBindingCount; |
+ |
+ // Moderate binding will make the service priority equal to the priority of a visible process |
+ // while the app is in the foreground. It will stay bound only while the app is in the |
+ // foreground to protect a background process from the system out-of-memory killer. |
+ @GuardedBy("mBindingLock") |
+ private final ChildServiceConnection mModerateBinding; |
+ |
+ @GuardedBy("mBindingLock") |
+ private boolean mWasOomProtectedOnUnbind; |
+ |
+ @VisibleForTesting |
+ ManagedChildProcessConnection(Context context, int number, boolean sandboxed, |
+ DeathCallback deathCallback, String serviceClassName, |
+ Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) { |
+ super(context, number, sandboxed, deathCallback, serviceClassName, |
+ childProcessCommonParameters, creationParams); |
+ |
+ int initialFlags = Context.BIND_AUTO_CREATE; |
+ int extraBindFlags = shouldBindAsExportedService() ? Context.BIND_EXTERNAL_SERVICE : 0; |
+ |
+ synchronized (mBindingLock) { |
+ mInitialBinding = createServiceConnection(initialFlags | extraBindFlags); |
+ mStrongBinding = createServiceConnection( |
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags); |
+ mWaivedBinding = createServiceConnection( |
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags); |
+ mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags); |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean bind() { |
+ synchronized (mBindingLock) { |
+ if (!mInitialBinding.bind()) { |
+ return false; |
+ } |
+ mWaivedBinding.bind(); |
+ } |
+ return true; |
+ } |
+ |
+ @Override |
+ public void unbind() { |
+ synchronized (mBindingLock) { |
+ if (!isBound()) { |
+ return; |
+ } |
+ mWasOomProtectedOnUnbind = isCurrentlyOomProtected(); |
+ mInitialBinding.unbind(); |
+ mStrongBinding.unbind(); |
+ mWaivedBinding.unbind(); |
+ mModerateBinding.unbind(); |
+ mStrongBindingCount = 0; |
+ } |
+ } |
+ |
+ @GuardedBy("mBindingLock") |
+ private boolean isBound() { |
+ return mInitialBinding.isBound() || mStrongBinding.isBound() || mWaivedBinding.isBound() |
+ || mModerateBinding.isBound(); |
+ } |
+ |
+ public boolean isInitialBindingBound() { |
+ synchronized (mBindingLock) { |
+ return mInitialBinding.isBound(); |
+ } |
+ } |
+ |
+ public boolean isStrongBindingBound() { |
+ synchronized (mBindingLock) { |
+ return mStrongBinding.isBound(); |
+ } |
+ } |
+ |
+ public void removeInitialBinding() { |
+ synchronized (mBindingLock) { |
+ mInitialBinding.unbind(); |
+ } |
+ } |
+ |
+ public boolean isOomProtectedOrWasWhenDied() { |
+ // Call isConnected() outside of the synchronized block or we could deadlock. |
+ final boolean isConnected = isConnected(); |
+ synchronized (mBindingLock) { |
+ if (isConnected) { |
+ return isCurrentlyOomProtected(); |
+ } |
+ return mWasOomProtectedOnUnbind; |
+ } |
+ } |
+ |
+ @GuardedBy("mBindingLock") |
+ private boolean isCurrentlyOomProtected() { |
+ return mInitialBinding.isBound() || mStrongBinding.isBound(); |
+ } |
+ |
+ public void dropOomBindings() { |
+ synchronized (mBindingLock) { |
+ mInitialBinding.unbind(); |
+ |
+ mStrongBindingCount = 0; |
+ mStrongBinding.unbind(); |
+ |
+ mModerateBinding.unbind(); |
+ } |
+ } |
+ |
+ public void addStrongBinding() { |
+ // Call isConnected() outside of the synchronized block or we could deadlock. |
+ final boolean isConnected = isConnected(); |
+ synchronized (mBindingLock) { |
+ if (!isConnected) { |
+ Log.w(TAG, "The connection is not bound for %d", getPid()); |
+ return; |
+ } |
+ if (mStrongBindingCount == 0) { |
+ mStrongBinding.bind(); |
+ } |
+ mStrongBindingCount++; |
+ } |
+ } |
+ |
+ public void removeStrongBinding() { |
+ // Call isConnected() outside of the synchronized block or we could deadlock. |
+ final boolean isConnected = isConnected(); |
+ synchronized (mBindingLock) { |
+ if (!isConnected) { |
+ Log.w(TAG, "The connection is not bound for %d", getPid()); |
+ return; |
+ } |
+ assert mStrongBindingCount > 0; |
+ mStrongBindingCount--; |
+ if (mStrongBindingCount == 0) { |
+ mStrongBinding.unbind(); |
+ } |
+ } |
+ } |
+ |
+ public boolean isModerateBindingBound() { |
+ synchronized (mBindingLock) { |
+ return mModerateBinding.isBound(); |
+ } |
+ } |
+ |
+ public void addModerateBinding() { |
+ // Call isConnected() outside of the synchronized block or we could deadlock. |
+ final boolean isConnected = isConnected(); |
+ synchronized (mBindingLock) { |
+ if (!isConnected) { |
+ Log.w(TAG, "The connection is not bound for %d", getPid()); |
+ return; |
+ } |
+ mModerateBinding.bind(); |
+ } |
+ } |
+ |
+ public void removeModerateBinding() { |
+ // Call isConnected() outside of the synchronized block or we could deadlock. |
+ final boolean isConnected = isConnected(); |
+ synchronized (mBindingLock) { |
+ if (!isConnected) { |
+ Log.w(TAG, "The connection is not bound for %d", getPid()); |
+ return; |
+ } |
+ mModerateBinding.unbind(); |
+ } |
+ } |
+} |