| Index: content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
|
| deleted file mode 100644
|
| index 583a76c8a508f442f3553b04748dc1e8b4b23016..0000000000000000000000000000000000000000
|
| --- a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java
|
| +++ /dev/null
|
| @@ -1,465 +0,0 @@
|
| -// Copyright 2014 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.annotation.TargetApi;
|
| -import android.content.ComponentCallbacks2;
|
| -import android.content.Context;
|
| -import android.content.res.Configuration;
|
| -import android.os.Build;
|
| -import android.util.LruCache;
|
| -import android.util.SparseArray;
|
| -
|
| -import org.chromium.base.Log;
|
| -import org.chromium.base.SysUtils;
|
| -import org.chromium.base.ThreadUtils;
|
| -import org.chromium.base.VisibleForTesting;
|
| -import org.chromium.base.metrics.RecordHistogram;
|
| -
|
| -import java.util.Map;
|
| -
|
| -/**
|
| - * Manages oom bindings used to bound child services.
|
| - * This object must only be accessed from the launcher thread.
|
| - */
|
| -class BindingManagerImpl implements BindingManager {
|
| - private static final String TAG = "cr.BindingManager";
|
| -
|
| - // Low reduce ratio of moderate binding.
|
| - private static final float MODERATE_BINDING_LOW_REDUCE_RATIO = 0.25f;
|
| - // High reduce ratio of moderate binding.
|
| - private static final float MODERATE_BINDING_HIGH_REDUCE_RATIO = 0.5f;
|
| -
|
| - // Delay of 1 second used when removing temporary strong binding of a process (only on
|
| - // non-low-memory devices).
|
| - private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000;
|
| -
|
| - // Delays used when clearing moderate binding pool when onSentToBackground happens.
|
| - private static final long MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS = 10 * 1000;
|
| -
|
| - // These fields allow to override the parameters for testing - see
|
| - // createBindingManagerForTesting().
|
| - private final boolean mIsLowMemoryDevice;
|
| -
|
| - private static class ModerateBindingPool
|
| - extends LruCache<Integer, ManagedConnection> implements ComponentCallbacks2 {
|
| - private Runnable mDelayedClearer;
|
| -
|
| - public ModerateBindingPool(int maxSize) {
|
| - super(maxSize);
|
| - }
|
| -
|
| - @Override
|
| - public void onTrimMemory(final int level) {
|
| - ThreadUtils.assertOnUiThread();
|
| - LauncherThread.post(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - Log.i(TAG, "onTrimMemory: level=%d, size=%d", level, size());
|
| - if (size() <= 0) {
|
| - return;
|
| - }
|
| - if (level <= TRIM_MEMORY_RUNNING_MODERATE) {
|
| - reduce(MODERATE_BINDING_LOW_REDUCE_RATIO);
|
| - } else if (level <= TRIM_MEMORY_RUNNING_LOW) {
|
| - reduce(MODERATE_BINDING_HIGH_REDUCE_RATIO);
|
| - } else if (level == TRIM_MEMORY_UI_HIDDEN) {
|
| - // This will be handled by |mDelayedClearer|.
|
| - return;
|
| - } else {
|
| - evictAll();
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - @Override
|
| - public void onLowMemory() {
|
| - ThreadUtils.assertOnUiThread();
|
| - LauncherThread.post(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - Log.i(TAG, "onLowMemory: evict %d bindings", size());
|
| - evictAll();
|
| - }
|
| - });
|
| - }
|
| -
|
| - @Override
|
| - public void onConfigurationChanged(Configuration configuration) {}
|
| -
|
| - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
| - private void reduce(float reduceRatio) {
|
| - int oldSize = size();
|
| - int newSize = (int) (oldSize * (1f - reduceRatio));
|
| - Log.i(TAG, "Reduce connections from %d to %d", oldSize, newSize);
|
| - if (newSize == 0) {
|
| - evictAll();
|
| - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
| - trimToSize(newSize);
|
| - } else {
|
| - // Entries will be removed from the front because snapshot() returns ones ordered
|
| - // from least recently accessed to most recently accessed.
|
| - int count = 0;
|
| - for (Map.Entry<Integer, ManagedConnection> entry : snapshot().entrySet()) {
|
| - remove(entry.getKey());
|
| - ++count;
|
| - if (count == oldSize - newSize) break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - void addConnection(ManagedConnection managedConnection) {
|
| - ManagedChildProcessConnection connection = managedConnection.mConnection;
|
| - if (connection != null && connection.isSandboxed()) {
|
| - managedConnection.addModerateBinding();
|
| - if (connection.isModerateBindingBound()) {
|
| - put(connection.getServiceNumber(), managedConnection);
|
| - } else {
|
| - remove(connection.getServiceNumber());
|
| - }
|
| - }
|
| - }
|
| -
|
| - void removeConnection(ManagedConnection managedConnection) {
|
| - ManagedChildProcessConnection connection = managedConnection.mConnection;
|
| - if (connection != null && connection.isSandboxed()) {
|
| - remove(connection.getServiceNumber());
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void entryRemoved(boolean evicted, Integer key, ManagedConnection oldValue,
|
| - ManagedConnection newValue) {
|
| - if (oldValue != newValue) {
|
| - oldValue.removeModerateBinding();
|
| - }
|
| - }
|
| -
|
| - void onSentToBackground(final boolean onTesting) {
|
| - if (size() == 0) return;
|
| - mDelayedClearer = new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - if (mDelayedClearer == null) return;
|
| - mDelayedClearer = null;
|
| - Log.i(TAG, "Release moderate connections: %d", size());
|
| - if (!onTesting) {
|
| - RecordHistogram.recordCountHistogram(
|
| - "Android.ModerateBindingCount", size());
|
| - }
|
| - evictAll();
|
| - }
|
| - };
|
| - LauncherThread.postDelayed(mDelayedClearer, MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS);
|
| - }
|
| -
|
| - void onBroughtToForeground() {
|
| - if (mDelayedClearer != null) {
|
| - LauncherThread.removeCallbacks(mDelayedClearer);
|
| - mDelayedClearer = null;
|
| - }
|
| - }
|
| - }
|
| -
|
| - private ModerateBindingPool mModerateBindingPool;
|
| -
|
| - /**
|
| - * Wraps ManagedChildProcessConnection keeping track of additional information needed to manage
|
| - * the bindings of the connection. The reference to ManagedChildProcessConnection is cleared
|
| - * when the connection goes away, but ManagedConnection itself is kept (until overwritten by a
|
| - * new entry for the same pid).
|
| - */
|
| - private class ManagedConnection {
|
| - // Set in constructor, cleared in clearConnection() (on a separate thread).
|
| - // Need to keep a local reference to avoid it being cleared while using it.
|
| - private ManagedChildProcessConnection mConnection;
|
| -
|
| - // True iff there is a strong binding kept on the service because it is working in
|
| - // foreground.
|
| - private boolean mInForeground;
|
| -
|
| - // True iff there is a strong binding kept on the service because it was bound for the
|
| - // application background period.
|
| - private boolean mBoundForBackgroundPeriod;
|
| -
|
| - /**
|
| - * Removes the initial service binding.
|
| - * @return true if the binding was removed.
|
| - */
|
| - private boolean removeInitialBinding() {
|
| - ManagedChildProcessConnection connection = mConnection;
|
| - if (connection == null || !connection.isInitialBindingBound()) return false;
|
| -
|
| - connection.removeInitialBinding();
|
| - return true;
|
| - }
|
| -
|
| - /** Adds a strong service binding. */
|
| - private void addStrongBinding() {
|
| - ManagedChildProcessConnection connection = mConnection;
|
| - if (connection == null) return;
|
| -
|
| - connection.addStrongBinding();
|
| - if (mModerateBindingPool != null) mModerateBindingPool.removeConnection(this);
|
| - }
|
| -
|
| - /** Removes a strong service binding. */
|
| - private void removeStrongBinding(final boolean keepAsModerate) {
|
| - final ManagedChildProcessConnection connection = mConnection;
|
| - // We have to fail gracefully if the strong binding is not present, as on low-end the
|
| - // binding could have been removed by dropOomBindings() when a new service was started.
|
| - if (connection == null || !connection.isStrongBindingBound()) return;
|
| -
|
| - // This runnable performs the actual unbinding. It will be executed synchronously when
|
| - // on low-end devices and posted with a delay otherwise.
|
| - Runnable doUnbind = new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - if (connection.isStrongBindingBound()) {
|
| - connection.removeStrongBinding();
|
| - if (keepAsModerate) {
|
| - addConnectionToModerateBindingPool(connection);
|
| - }
|
| - }
|
| - }
|
| - };
|
| -
|
| - if (mIsLowMemoryDevice) {
|
| - doUnbind.run();
|
| - } else {
|
| - LauncherThread.postDelayed(doUnbind, DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Adds connection to the moderate binding pool. No-op if the connection has a strong
|
| - * binding.
|
| - * @param connection The ChildProcessConnection to add to the moderate binding pool.
|
| - */
|
| - private void addConnectionToModerateBindingPool(ManagedChildProcessConnection connection) {
|
| - if (mModerateBindingPool != null && !connection.isStrongBindingBound()) {
|
| - mModerateBindingPool.addConnection(ManagedConnection.this);
|
| - }
|
| - }
|
| -
|
| - /** Removes the moderate service binding. */
|
| - private void removeModerateBinding() {
|
| - ManagedChildProcessConnection connection = mConnection;
|
| - if (connection == null || !connection.isModerateBindingBound()) return;
|
| - connection.removeModerateBinding();
|
| - }
|
| -
|
| - /** Adds the moderate service binding. */
|
| - private void addModerateBinding() {
|
| - ManagedChildProcessConnection connection = mConnection;
|
| - if (connection == null) return;
|
| -
|
| - connection.addModerateBinding();
|
| - }
|
| -
|
| - /**
|
| - * Drops the service bindings. This is used on low-end to drop bindings of the current
|
| - * service when a new one is used in foreground.
|
| - */
|
| - private void dropBindings() {
|
| - assert mIsLowMemoryDevice;
|
| - ManagedChildProcessConnection connection = mConnection;
|
| - if (connection == null) return;
|
| -
|
| - connection.dropOomBindings();
|
| - }
|
| -
|
| - ManagedConnection(ManagedChildProcessConnection connection) {
|
| - mConnection = connection;
|
| - }
|
| -
|
| - /**
|
| - * Sets the visibility of the service, adding or removing the strong binding as needed.
|
| - */
|
| - void setInForeground(boolean nextInForeground) {
|
| - if (!mInForeground && nextInForeground) {
|
| - addStrongBinding();
|
| - } else if (mInForeground && !nextInForeground) {
|
| - removeStrongBinding(true);
|
| - }
|
| -
|
| - mInForeground = nextInForeground;
|
| - }
|
| -
|
| - /**
|
| - * Called when it is safe to rely on setInForeground() for binding management.
|
| - */
|
| - void onDeterminedVisibility() {
|
| - if (!removeInitialBinding()) return;
|
| - // Decrease the likelihood of a recently created background tab getting evicted by
|
| - // immediately adding moderate binding.
|
| - addConnectionToModerateBindingPool(mConnection);
|
| - }
|
| -
|
| - /**
|
| - * Sets or removes additional binding when the service is main service during the embedder
|
| - * background period.
|
| - */
|
| - void setBoundForBackgroundPeriod(boolean nextBound) {
|
| - if (!mBoundForBackgroundPeriod && nextBound) {
|
| - addStrongBinding();
|
| - } else if (mBoundForBackgroundPeriod && !nextBound) {
|
| - removeStrongBinding(false);
|
| - }
|
| -
|
| - mBoundForBackgroundPeriod = nextBound;
|
| - }
|
| -
|
| - void clearConnection() {
|
| - if (mModerateBindingPool != null) mModerateBindingPool.removeConnection(this);
|
| - mConnection = null;
|
| - }
|
| - }
|
| -
|
| - private final SparseArray<ManagedConnection> mManagedConnections =
|
| - new SparseArray<ManagedConnection>();
|
| -
|
| - // The connection that was most recently set as foreground (using setInForeground()). This is
|
| - // used to add additional binding on it when the embedder goes to background. On low-end, this
|
| - // is also used to drop process bindings when a new one is created, making sure that only one
|
| - // renderer process at a time is protected from oom killing.
|
| - private ManagedConnection mLastInForeground;
|
| -
|
| - // The connection bound with additional binding in onSentToBackground().
|
| - private ManagedConnection mBoundForBackgroundPeriod;
|
| -
|
| - // Whether this instance is used on testing.
|
| - private final boolean mOnTesting;
|
| -
|
| - /**
|
| - * The constructor is private to hide parameters exposed for testing from the regular consumer.
|
| - * Use factory methods to create an instance.
|
| - */
|
| - private BindingManagerImpl(boolean isLowMemoryDevice, boolean onTesting) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - mIsLowMemoryDevice = isLowMemoryDevice;
|
| - mOnTesting = onTesting;
|
| - }
|
| -
|
| - public static BindingManagerImpl createBindingManager() {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - return new BindingManagerImpl(SysUtils.isLowEndDevice(), false);
|
| - }
|
| -
|
| - /**
|
| - * Creates a testing instance of BindingManager. Testing instance will have the unbinding delays
|
| - * set to 0, so that the tests don't need to deal with actual waiting.
|
| - * @param isLowEndDevice true iff the created instance should apply low-end binding policies
|
| - */
|
| - public static BindingManagerImpl createBindingManagerForTesting(boolean isLowEndDevice) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - return new BindingManagerImpl(isLowEndDevice, true);
|
| - }
|
| -
|
| - @Override
|
| - public void addNewConnection(int pid, ManagedChildProcessConnection connection) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - // This will reset the previous entry for the pid in the unlikely event of the OS
|
| - // reusing renderer pids.
|
| - mManagedConnections.put(pid, new ManagedConnection(connection));
|
| - }
|
| -
|
| - @Override
|
| - public void setInForeground(int pid, boolean inForeground) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - ManagedConnection managedConnection = mManagedConnections.get(pid);
|
| - if (managedConnection == null) {
|
| - Log.w(TAG, "Cannot setInForeground() - never saw a connection for the pid: %d", pid);
|
| - return;
|
| - }
|
| -
|
| - if (inForeground && mIsLowMemoryDevice && mLastInForeground != null
|
| - && mLastInForeground != managedConnection) {
|
| - mLastInForeground.dropBindings();
|
| - }
|
| -
|
| - managedConnection.setInForeground(inForeground);
|
| - if (inForeground) mLastInForeground = managedConnection;
|
| - }
|
| -
|
| - @Override
|
| - public void onDeterminedVisibility(int pid) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - ManagedConnection managedConnection = mManagedConnections.get(pid);
|
| - if (managedConnection == null) {
|
| - Log.w(TAG, "Cannot call determinedVisibility() - never saw a connection for the pid: "
|
| - + "%d", pid);
|
| - return;
|
| - }
|
| -
|
| - managedConnection.onDeterminedVisibility();
|
| - }
|
| -
|
| - @Override
|
| - public void onSentToBackground() {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - assert mBoundForBackgroundPeriod == null;
|
| - // mLastInForeground can be null at this point as the embedding application could be
|
| - // used in foreground without spawning any renderers.
|
| - if (mLastInForeground != null) {
|
| - mLastInForeground.setBoundForBackgroundPeriod(true);
|
| - mBoundForBackgroundPeriod = mLastInForeground;
|
| - }
|
| - if (mModerateBindingPool != null) mModerateBindingPool.onSentToBackground(mOnTesting);
|
| - }
|
| -
|
| - @Override
|
| - public void onBroughtToForeground() {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - if (mBoundForBackgroundPeriod != null) {
|
| - mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false);
|
| - mBoundForBackgroundPeriod = null;
|
| - }
|
| - if (mModerateBindingPool != null) mModerateBindingPool.onBroughtToForeground();
|
| - }
|
| -
|
| - @Override
|
| - public void removeConnection(int pid) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - ManagedConnection managedConnection = mManagedConnections.get(pid);
|
| - if (managedConnection != null) {
|
| - mManagedConnections.remove(pid);
|
| - managedConnection.clearConnection();
|
| - }
|
| - }
|
| -
|
| - /** @return true iff the connection reference is no longer held */
|
| - @VisibleForTesting
|
| - public boolean isConnectionCleared(int pid) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - return mManagedConnections.get(pid) == null;
|
| - }
|
| -
|
| - @Override
|
| - public void startModerateBindingManagement(Context context, int maxSize) {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - if (mIsLowMemoryDevice) return;
|
| -
|
| - if (mModerateBindingPool == null) {
|
| - Log.i(TAG, "Moderate binding enabled: maxSize=%d", maxSize);
|
| - mModerateBindingPool = new ModerateBindingPool(maxSize);
|
| - if (context != null) {
|
| - // Note that it is safe to call Context.registerComponentCallbacks from a background
|
| - // thread.
|
| - context.registerComponentCallbacks(mModerateBindingPool);
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void releaseAllModerateBindings() {
|
| - assert LauncherThread.runningOnLauncherThread();
|
| - if (mModerateBindingPool != null) {
|
| - Log.i(TAG, "Release all moderate bindings: %d", mModerateBindingPool.size());
|
| - mModerateBindingPool.evictAll();
|
| - }
|
| - }
|
| -}
|
|
|