 Chromium Code Reviews
 Chromium Code Reviews Issue 1146813010:
  Introduce moderate binding  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1146813010:
  Introduce moderate binding  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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..d167541b7caadfb3679b7d33cc60f30a51505d85 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,27 @@ | 
| package org.chromium.content.browser; | 
| -import android.util.Log; | 
| +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. | 
| */ | 
| 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 +35,90 @@ 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) {} | 
| + | 
| + @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 " + oldSize + " to " + newSize); | 
| + if (newSize == 0) { | 
| + evictAll(); | 
| + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | 
| + trimToSize(newSize); | 
| + } else { | 
| 
Jaekyun Seok (inactive)
2015/06/22 03:17:52
I realized that LruCache.trimToSize is available o
 | 
| + int count = 0; | 
| + for (Map.Entry<Integer, ManagedConnection> entry : snapshot().entrySet()) { | 
| 
Alexei Svitkine (slow)
2015/06/22 14:58:52
Nit: Add a comment explaining why this makes sense
 
Jaekyun Seok (inactive)
2015/06/22 21:24:48
Done.
 | 
| + remove(entry.getKey()); | 
| + ++count; | 
| + if (count == oldSize - newSize) break; | 
| + } | 
| + } | 
| + } | 
| + | 
| + 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 +152,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 +171,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 +186,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 +223,7 @@ class BindingManagerImpl implements BindingManager { | 
| if (!mInForeground && nextInForeground) { | 
| addStrongBinding(); | 
| } else if (mInForeground && !nextInForeground) { | 
| - removeStrongBinding(); | 
| + removeStrongBinding(true); | 
| } | 
| mInForeground = nextInForeground; | 
| @@ -130,7 +244,7 @@ class BindingManagerImpl implements BindingManager { | 
| if (!mBoundForBackgroundPeriod && nextBound) { | 
| addStrongBinding(); | 
| } else if (mBoundForBackgroundPeriod && !nextBound) { | 
| - removeStrongBinding(); | 
| + removeStrongBinding(false); | 
| } | 
| mBoundForBackgroundPeriod = nextBound; | 
| @@ -146,6 +260,9 @@ class BindingManagerImpl implements BindingManager { | 
| void clearConnection() { | 
| mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); | 
| + if (mModerateBindingPool != null) { | 
| + mModerateBindingPool.removeConnection(this); | 
| + } | 
| mConnection = null; | 
| } | 
| @@ -173,18 +290,23 @@ class BindingManagerImpl implements BindingManager { | 
| // 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, 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 +315,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 +378,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 +424,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); | 
| + } | 
| } |