Chromium Code Reviews| 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 |
| index 728699e38e7192555aeca66f39eaa39dfa456517..e78f02b055859c2673e1b0463b87167fbe944a81 100644 |
| --- a/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java |
| +++ b/content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java |
| @@ -4,18 +4,23 @@ |
| package org.chromium.content.browser; |
| -import android.util.Log; |
| +import android.content.ComponentCallbacks2; |
| +import android.content.Context; |
| +import android.content.res.Configuration; |
| +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; |
| /** |
| * Manages oom bindings used to bound child services. |
| */ |
| class BindingManagerImpl implements BindingManager { |
| - private static final String TAG = "BindingManager"; |
| + private static final String TAG = "cr.BindingManager"; |
| // Delay of 1 second used when removing temporary strong binding of a process (only on |
| // non-low-memory devices). |
| @@ -26,6 +31,81 @@ class BindingManagerImpl implements BindingManager { |
| private final long mRemoveStrongBindingDelay; |
| private final boolean mIsLowMemoryDevice; |
| + private static class ModerateBindingPool |
| + extends LruCache<Integer, ManagedConnection> implements ComponentCallbacks2 { |
| + private final float mLowReduceRatio; |
| + private final float mHighReduceRatio; |
| + |
| + public ModerateBindingPool(int maxSize, float lowReduceRatio, float highReduceRatio) { |
| + super(maxSize); |
| + |
| + mLowReduceRatio = lowReduceRatio; |
| + mHighReduceRatio = highReduceRatio; |
| + } |
| + |
| + @Override |
| + public void onTrimMemory(int level) { |
| + Log.i(TAG, "onTrimMemory: level=" + level + ", size=" + size()); |
| + if (size() > 0) { |
| + if (level <= TRIM_MEMORY_RUNNING_MODERATE) { |
| + reduce(mLowReduceRatio); |
| + } else if (level <= TRIM_MEMORY_RUNNING_LOW) { |
| + reduce(mHighReduceRatio); |
| + } else { |
| + evictAll(); |
| + } |
| + } |
| + } |
| + |
| + @Override |
| + public void onLowMemory() { |
| + Log.i(TAG, "onLowMemory: evict " + size() + " bindings"); |
| + evictAll(); |
| + } |
| + |
| + @Override |
| + public void onConfigurationChanged(Configuration configuration) {} |
| + |
| + private void reduce(float reduceRatio) { |
| + int newSize = (int) (size() * (1f - reduceRatio)); |
| + Log.i(TAG, "Reduce connections from " + size() + " to " + newSize); |
| + if (newSize == 0) { |
| + evictAll(); |
| + } else { |
| + trimToSize(newSize); |
| + } |
| + } |
| + |
| + void addConnection(ManagedConnection managedConnection) { |
| + ChildProcessConnection connection = managedConnection.mConnection; |
| + if (connection != null && connection.isInSandbox()) { |
| + managedConnection.addModerateBinding(); |
| + if (connection.isModerateBindingBound()) { |
| + put(connection.getServiceNumber(), managedConnection); |
| + } else { |
| + remove(connection.getServiceNumber()); |
| + } |
| + } |
| + } |
| + |
| + void removeConnection(ManagedConnection managedConnection) { |
| + ChildProcessConnection connection = managedConnection.mConnection; |
| + if (connection != null && connection.isInSandbox()) { |
| + remove(connection.getServiceNumber()); |
| + } |
| + } |
| + |
| + @Override |
| + protected void entryRemoved(boolean evicted, Integer key, ManagedConnection oldValue, |
| + ManagedConnection newValue) { |
| + if (oldValue != newValue) { |
| + oldValue.removeModerateBinding(); |
| + } |
| + } |
| + } |
| + |
| + private ModerateBindingPool mModerateBindingPool; |
| + |
| /** |
| * Wraps ChildProcessConnection keeping track of additional information needed to manage the |
| * bindings of the connection. The reference to ChildProcessConnection is cleared when the |
| @@ -59,10 +139,13 @@ class BindingManagerImpl implements BindingManager { |
| if (connection == null) return; |
| connection.addStrongBinding(); |
| + if (mModerateBindingPool != null) { |
| + mModerateBindingPool.removeConnection(this); |
| + } |
| } |
| /** Removes a strong service binding. */ |
| - private void removeStrongBinding() { |
| + private void removeStrongBinding(final boolean keepsAsModerate) { |
| final ChildProcessConnection 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. |
| @@ -75,6 +158,10 @@ class BindingManagerImpl implements BindingManager { |
| public void run() { |
| if (connection.isStrongBindingBound()) { |
| connection.removeStrongBinding(); |
| + if (mModerateBindingPool != null && !connection.isStrongBindingBound() |
| + && keepsAsModerate) { |
| + mModerateBindingPool.addConnection(ManagedConnection.this); |
| + } |
| } |
| } |
| }; |
| @@ -86,6 +173,20 @@ class BindingManagerImpl implements BindingManager { |
| } |
| } |
| + /** Removes the moderate service binding. */ |
| + private void removeModerateBinding() { |
| + if (mConnection == null || !mConnection.isModerateBindingBound()) return; |
| + mConnection.removeModerateBinding(); |
| + } |
| + |
| + /** Adds the moderate service binding. */ |
| + private void addModerateBinding() { |
| + ChildProcessConnection 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. |
| @@ -109,7 +210,7 @@ class BindingManagerImpl implements BindingManager { |
| if (!mInForeground && nextInForeground) { |
| addStrongBinding(); |
| } else if (mInForeground && !nextInForeground) { |
| - removeStrongBinding(); |
| + removeStrongBinding(true); |
| } |
| mInForeground = nextInForeground; |
| @@ -130,7 +231,7 @@ class BindingManagerImpl implements BindingManager { |
| if (!mBoundForBackgroundPeriod && nextBound) { |
| addStrongBinding(); |
| } else if (mBoundForBackgroundPeriod && !nextBound) { |
| - removeStrongBinding(); |
| + removeStrongBinding(false); |
| } |
| mBoundForBackgroundPeriod = nextBound; |
| @@ -146,6 +247,9 @@ class BindingManagerImpl implements BindingManager { |
| void clearConnection() { |
| mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); |
| + if (mModerateBindingPool != null) { |
| + mModerateBindingPool.removeConnection(this); |
| + } |
| mConnection = null; |
| } |
| @@ -173,18 +277,23 @@ class BindingManagerImpl implements BindingManager { |
| // The connection bound with additional binding in onSentToBackground(). |
| private ManagedConnection mBoundForBackgroundPeriod; |
| + // Whether this instance is used on testing. |
| + private boolean mOnTesting; |
|
Yaron
2015/06/17 02:56:12
final
Jaekyun Seok (inactive)
2015/06/17 04:35:46
Done.
|
| + |
| /** |
| * 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, long removeStrongBindingDelay) { |
| + private BindingManagerImpl( |
| + boolean isLowMemoryDevice, long removeStrongBindingDelay, boolean onTesting) { |
| mIsLowMemoryDevice = isLowMemoryDevice; |
| mRemoveStrongBindingDelay = removeStrongBindingDelay; |
| + mOnTesting = onTesting; |
| } |
| public static BindingManagerImpl createBindingManager() { |
| - return new BindingManagerImpl(SysUtils.isLowEndDevice(), |
| - DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS); |
| + return new BindingManagerImpl( |
| + SysUtils.isLowEndDevice(), DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS, false); |
| } |
| /** |
| @@ -193,7 +302,7 @@ class BindingManagerImpl implements BindingManager { |
| * @param isLowEndDevice true iff the created instance should apply low-end binding policies |
| */ |
| public static BindingManagerImpl createBindingManagerForTesting(boolean isLowEndDevice) { |
| - return new BindingManagerImpl(isLowEndDevice, 0); |
| + return new BindingManagerImpl(isLowEndDevice, 0, true); |
| } |
| @Override |
| @@ -256,6 +365,14 @@ class BindingManagerImpl implements BindingManager { |
| mBoundForBackgroundPeriod = mLastInForeground; |
| } |
| } |
| + if (mModerateBindingPool != null) { |
| + Log.i(TAG, "Release moderate connections: " + mModerateBindingPool.size()); |
| + if (!mOnTesting) { |
| + RecordHistogram.recordCountHistogram( |
| + "Android.ModerateBindingCount", mModerateBindingPool.size()); |
| + } |
| + mModerateBindingPool.evictAll(); |
| + } |
| } |
| @Override |
| @@ -294,4 +411,15 @@ class BindingManagerImpl implements BindingManager { |
| return mManagedConnections.get(pid).isConnectionCleared(); |
| } |
| } |
| + |
| + @Override |
| + public void startModerateBindingManagement( |
| + Context context, int maxSize, float lowReduceRatio, float highReduceRatio) { |
| + if (mIsLowMemoryDevice || mModerateBindingPool != null) return; |
| + |
| + Log.i(TAG, "Moderate binding enabled: maxSize=" + maxSize + " lowReduceRatio=" |
| + + lowReduceRatio + " highReduceRatio=" + highReduceRatio); |
| + mModerateBindingPool = new ModerateBindingPool(maxSize, lowReduceRatio, highReduceRatio); |
| + if (context != null) context.registerComponentCallbacks(mModerateBindingPool); |
| + } |
| } |