Chromium Code Reviews| 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..dc6cc8c049e11b39c4c3db73d6d46a0a0573ab46 |
| --- /dev/null |
| +++ b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java |
| @@ -0,0 +1,219 @@ |
| +// 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.Build; |
| +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) { |
| + ManagedChildProcessConnection connection = |
| + new ManagedChildProcessConnection(context, number, sandboxed, deathCallback, |
| + serviceClassName, childProcessCommonParameters, creationParams); |
| + connection.initBindings(); |
| + return connection; |
| + } |
| + }; |
| + |
| + // 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 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 ChildServiceConnection mStrongBinding; |
| + |
| + // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls |
| + // to start() and stop(). |
| + @GuardedBy("mBindingLock") |
| + private 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 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); |
| + } |
| + |
| + @VisibleForTesting |
| + protected void initBindings() { |
|
boliu
2017/04/20 22:33:34
again, nothing is overriding this? can just be par
Jay Civelli
2017/04/25 06:02:46
Oh right, we can do this now. (I used that origina
|
| + int initialFlags = Context.BIND_AUTO_CREATE; |
| + int extraBindFlags = 0; |
| + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationParams() != null |
|
boliu
2017/04/20 22:33:34
avoid duplicating this if condition, just expand i
Jay Civelli
2017/04/25 06:02:46
Done.
|
| + && getCreationParams().getIsExternalService() |
| + && isExportedService(isSandboxed(), getContext(), getServiceName())) { |
| + extraBindFlags = Context.BIND_EXTERNAL_SERVICE; |
| + } |
| + |
| + 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) { |
| + mWasOomProtectedOnUnbind = isCurrentlyOomProtected(); |
| + mInitialBinding.unbind(); |
| + mStrongBinding.unbind(); |
| + mWaivedBinding.unbind(); |
| + mModerateBinding.unbind(); |
| + mStrongBindingCount = 0; |
| + } |
| + } |
| + |
| + 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() { |
| + synchronized (mBindingLock) { |
| + if (isConnected()) { |
| + return isCurrentlyOomProtected(); |
| + } |
| + return mWasOomProtectedOnUnbind; |
| + } |
| + } |
| + |
| + private boolean isCurrentlyOomProtected() { |
| + synchronized (mBindingLock) { |
| + return mInitialBinding.isBound() || mStrongBinding.isBound(); |
| + } |
| + } |
| + |
| + public void dropOomBindings() { |
| + synchronized (mBindingLock) { |
| + mInitialBinding.unbind(); |
| + |
| + mStrongBindingCount = 0; |
| + mStrongBinding.unbind(); |
| + |
| + mModerateBinding.unbind(); |
| + } |
| + } |
| + |
| + public void addStrongBinding() { |
| + 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() { |
| + 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() { |
| + synchronized (mBindingLock) { |
| + if (!isConnected()) { |
| + Log.w(TAG, "The connection is not bound for %d", getPid()); |
| + return; |
| + } |
| + mModerateBinding.bind(); |
| + } |
| + } |
| + |
| + public void removeModerateBinding() { |
| + synchronized (mBindingLock) { |
| + if (!isConnected()) { |
| + Log.w(TAG, "The connection is not bound for %d", getPid()); |
| + return; |
| + } |
| + mModerateBinding.unbind(); |
| + } |
| + } |
| +} |