Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
| 6 | 6 |
| 7 import android.util.Log; | 7 import android.content.ComponentCallbacks2; |
| 8 import android.content.Context; | |
| 9 import android.content.res.Configuration; | |
| 10 import android.util.LruCache; | |
| 8 import android.util.SparseArray; | 11 import android.util.SparseArray; |
| 9 | 12 |
| 13 import org.chromium.base.Log; | |
| 10 import org.chromium.base.SysUtils; | 14 import org.chromium.base.SysUtils; |
| 11 import org.chromium.base.ThreadUtils; | 15 import org.chromium.base.ThreadUtils; |
| 12 import org.chromium.base.VisibleForTesting; | 16 import org.chromium.base.VisibleForTesting; |
| 17 import org.chromium.base.metrics.RecordHistogram; | |
| 13 | 18 |
| 14 /** | 19 /** |
| 15 * Manages oom bindings used to bound child services. | 20 * Manages oom bindings used to bound child services. |
| 16 */ | 21 */ |
| 17 class BindingManagerImpl implements BindingManager { | 22 class BindingManagerImpl implements BindingManager { |
| 18 private static final String TAG = "BindingManager"; | 23 private static final String TAG = "cr.BindingManager"; |
| 19 | 24 |
| 20 // Delay of 1 second used when removing temporary strong binding of a proces s (only on | 25 // Delay of 1 second used when removing temporary strong binding of a proces s (only on |
| 21 // non-low-memory devices). | 26 // non-low-memory devices). |
| 22 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000; | 27 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000; |
| 23 | 28 |
| 24 // These fields allow to override the parameters for testing - see | 29 // These fields allow to override the parameters for testing - see |
| 25 // createBindingManagerForTesting(). | 30 // createBindingManagerForTesting(). |
| 26 private final long mRemoveStrongBindingDelay; | 31 private final long mRemoveStrongBindingDelay; |
| 27 private final boolean mIsLowMemoryDevice; | 32 private final boolean mIsLowMemoryDevice; |
| 28 | 33 |
| 34 private static class ModerateBindingPool | |
| 35 extends LruCache<Integer, ManagedConnection> implements ComponentCal lbacks2 { | |
| 36 private final float mLowReduceRatio; | |
| 37 private final float mHighReduceRatio; | |
| 38 | |
| 39 public ModerateBindingPool(int maxSize, float lowReduceRatio, float high ReduceRatio) { | |
| 40 super(maxSize); | |
| 41 | |
| 42 mLowReduceRatio = lowReduceRatio; | |
| 43 mHighReduceRatio = highReduceRatio; | |
| 44 } | |
| 45 | |
| 46 @Override | |
| 47 public void onTrimMemory(int level) { | |
| 48 Log.i(TAG, "onTrimMemory: level=" + level + ", size=" + size()); | |
| 49 if (size() > 0) { | |
| 50 if (level <= TRIM_MEMORY_RUNNING_MODERATE) { | |
| 51 reduce(mLowReduceRatio); | |
| 52 } else if (level <= TRIM_MEMORY_RUNNING_LOW) { | |
| 53 reduce(mHighReduceRatio); | |
| 54 } else { | |
| 55 evictAll(); | |
| 56 } | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 @Override | |
| 61 public void onLowMemory() { | |
| 62 Log.i(TAG, "onLowMemory: evict " + size() + " bindings"); | |
| 63 evictAll(); | |
| 64 } | |
| 65 | |
| 66 @Override | |
| 67 public void onConfigurationChanged(Configuration configuration) {} | |
| 68 | |
| 69 private void reduce(float reduceRatio) { | |
| 70 int newSize = (int) (size() * (1f - reduceRatio)); | |
| 71 Log.i(TAG, "Reduce connections from " + size() + " to " + newSize); | |
| 72 if (newSize == 0) { | |
| 73 evictAll(); | |
| 74 } else { | |
| 75 trimToSize(newSize); | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 void addConnection(ManagedConnection managedConnection) { | |
| 80 ChildProcessConnection connection = managedConnection.mConnection; | |
| 81 if (connection != null && connection.isInSandbox()) { | |
| 82 managedConnection.addModerateBinding(); | |
| 83 if (connection.isModerateBindingBound()) { | |
| 84 put(connection.getServiceNumber(), managedConnection); | |
| 85 } else { | |
| 86 remove(connection.getServiceNumber()); | |
| 87 } | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 void removeConnection(ManagedConnection managedConnection) { | |
| 92 ChildProcessConnection connection = managedConnection.mConnection; | |
| 93 if (connection != null && connection.isInSandbox()) { | |
| 94 remove(connection.getServiceNumber()); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 @Override | |
| 99 protected void entryRemoved(boolean evicted, Integer key, ManagedConnect ion oldValue, | |
| 100 ManagedConnection newValue) { | |
| 101 if (oldValue != newValue) { | |
| 102 oldValue.removeModerateBinding(); | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 private ModerateBindingPool mModerateBindingPool; | |
| 108 | |
| 29 /** | 109 /** |
| 30 * Wraps ChildProcessConnection keeping track of additional information need ed to manage the | 110 * Wraps ChildProcessConnection keeping track of additional information need ed to manage the |
| 31 * bindings of the connection. The reference to ChildProcessConnection is cl eared when the | 111 * bindings of the connection. The reference to ChildProcessConnection is cl eared when the |
| 32 * connection goes away, but ManagedConnection itself is kept (until overwri tten by a new entry | 112 * connection goes away, but ManagedConnection itself is kept (until overwri tten by a new entry |
| 33 * for the same pid). | 113 * for the same pid). |
| 34 */ | 114 */ |
| 35 private class ManagedConnection { | 115 private class ManagedConnection { |
| 36 // Set in constructor, cleared in clearConnection(). | 116 // Set in constructor, cleared in clearConnection(). |
| 37 private ChildProcessConnection mConnection; | 117 private ChildProcessConnection mConnection; |
| 38 | 118 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 52 if (mConnection == null || !mConnection.isInitialBindingBound()) ret urn; | 132 if (mConnection == null || !mConnection.isInitialBindingBound()) ret urn; |
| 53 mConnection.removeInitialBinding(); | 133 mConnection.removeInitialBinding(); |
| 54 } | 134 } |
| 55 | 135 |
| 56 /** Adds a strong service binding. */ | 136 /** Adds a strong service binding. */ |
| 57 private void addStrongBinding() { | 137 private void addStrongBinding() { |
| 58 ChildProcessConnection connection = mConnection; | 138 ChildProcessConnection connection = mConnection; |
| 59 if (connection == null) return; | 139 if (connection == null) return; |
| 60 | 140 |
| 61 connection.addStrongBinding(); | 141 connection.addStrongBinding(); |
| 142 if (mModerateBindingPool != null) { | |
| 143 mModerateBindingPool.removeConnection(this); | |
| 144 } | |
| 62 } | 145 } |
| 63 | 146 |
| 64 /** Removes a strong service binding. */ | 147 /** Removes a strong service binding. */ |
| 65 private void removeStrongBinding() { | 148 private void removeStrongBinding(final boolean keepsAsModerate) { |
| 66 final ChildProcessConnection connection = mConnection; | 149 final ChildProcessConnection connection = mConnection; |
| 67 // We have to fail gracefully if the strong binding is not present, as on low-end the | 150 // We have to fail gracefully if the strong binding is not present, as on low-end the |
| 68 // binding could have been removed by dropOomBindings() when a new s ervice was started. | 151 // binding could have been removed by dropOomBindings() when a new s ervice was started. |
| 69 if (connection == null || !connection.isStrongBindingBound()) return ; | 152 if (connection == null || !connection.isStrongBindingBound()) return ; |
| 70 | 153 |
| 71 // This runnable performs the actual unbinding. It will be executed synchronously when | 154 // This runnable performs the actual unbinding. It will be executed synchronously when |
| 72 // on low-end devices and posted with a delay otherwise. | 155 // on low-end devices and posted with a delay otherwise. |
| 73 Runnable doUnbind = new Runnable() { | 156 Runnable doUnbind = new Runnable() { |
| 74 @Override | 157 @Override |
| 75 public void run() { | 158 public void run() { |
| 76 if (connection.isStrongBindingBound()) { | 159 if (connection.isStrongBindingBound()) { |
| 77 connection.removeStrongBinding(); | 160 connection.removeStrongBinding(); |
| 161 if (mModerateBindingPool != null && !connection.isStrong BindingBound() | |
| 162 && keepsAsModerate) { | |
| 163 mModerateBindingPool.addConnection(ManagedConnection .this); | |
| 164 } | |
| 78 } | 165 } |
| 79 } | 166 } |
| 80 }; | 167 }; |
| 81 | 168 |
| 82 if (mIsLowMemoryDevice) { | 169 if (mIsLowMemoryDevice) { |
| 83 doUnbind.run(); | 170 doUnbind.run(); |
| 84 } else { | 171 } else { |
| 85 ThreadUtils.postOnUiThreadDelayed(doUnbind, mRemoveStrongBinding Delay); | 172 ThreadUtils.postOnUiThreadDelayed(doUnbind, mRemoveStrongBinding Delay); |
| 86 } | 173 } |
| 87 } | 174 } |
| 88 | 175 |
| 176 /** Removes the moderate service binding. */ | |
| 177 private void removeModerateBinding() { | |
| 178 if (mConnection == null || !mConnection.isModerateBindingBound()) re turn; | |
| 179 mConnection.removeModerateBinding(); | |
| 180 } | |
| 181 | |
| 182 /** Adds the moderate service binding. */ | |
| 183 private void addModerateBinding() { | |
| 184 ChildProcessConnection connection = mConnection; | |
| 185 if (connection == null) return; | |
| 186 | |
| 187 connection.addModerateBinding(); | |
| 188 } | |
| 189 | |
| 89 /** | 190 /** |
| 90 * Drops the service bindings. This is used on low-end to drop bindings of the current | 191 * Drops the service bindings. This is used on low-end to drop bindings of the current |
| 91 * service when a new one is used in foreground. | 192 * service when a new one is used in foreground. |
| 92 */ | 193 */ |
| 93 private void dropBindings() { | 194 private void dropBindings() { |
| 94 assert mIsLowMemoryDevice; | 195 assert mIsLowMemoryDevice; |
| 95 ChildProcessConnection connection = mConnection; | 196 ChildProcessConnection connection = mConnection; |
| 96 if (connection == null) return; | 197 if (connection == null) return; |
| 97 | 198 |
| 98 connection.dropOomBindings(); | 199 connection.dropOomBindings(); |
| 99 } | 200 } |
| 100 | 201 |
| 101 ManagedConnection(ChildProcessConnection connection) { | 202 ManagedConnection(ChildProcessConnection connection) { |
| 102 mConnection = connection; | 203 mConnection = connection; |
| 103 } | 204 } |
| 104 | 205 |
| 105 /** | 206 /** |
| 106 * Sets the visibility of the service, adding or removing the strong bin ding as needed. | 207 * Sets the visibility of the service, adding or removing the strong bin ding as needed. |
| 107 */ | 208 */ |
| 108 void setInForeground(boolean nextInForeground) { | 209 void setInForeground(boolean nextInForeground) { |
| 109 if (!mInForeground && nextInForeground) { | 210 if (!mInForeground && nextInForeground) { |
| 110 addStrongBinding(); | 211 addStrongBinding(); |
| 111 } else if (mInForeground && !nextInForeground) { | 212 } else if (mInForeground && !nextInForeground) { |
| 112 removeStrongBinding(); | 213 removeStrongBinding(true); |
| 113 } | 214 } |
| 114 | 215 |
| 115 mInForeground = nextInForeground; | 216 mInForeground = nextInForeground; |
| 116 } | 217 } |
| 117 | 218 |
| 118 /** | 219 /** |
| 119 * Removes the initial binding. | 220 * Removes the initial binding. |
| 120 */ | 221 */ |
| 121 void determinedVisibility() { | 222 void determinedVisibility() { |
| 122 removeInitialBinding(); | 223 removeInitialBinding(); |
| 123 } | 224 } |
| 124 | 225 |
| 125 /** | 226 /** |
| 126 * Sets or removes additional binding when the service is main service d uring the embedder | 227 * Sets or removes additional binding when the service is main service d uring the embedder |
| 127 * background period. | 228 * background period. |
| 128 */ | 229 */ |
| 129 void setBoundForBackgroundPeriod(boolean nextBound) { | 230 void setBoundForBackgroundPeriod(boolean nextBound) { |
| 130 if (!mBoundForBackgroundPeriod && nextBound) { | 231 if (!mBoundForBackgroundPeriod && nextBound) { |
| 131 addStrongBinding(); | 232 addStrongBinding(); |
| 132 } else if (mBoundForBackgroundPeriod && !nextBound) { | 233 } else if (mBoundForBackgroundPeriod && !nextBound) { |
| 133 removeStrongBinding(); | 234 removeStrongBinding(false); |
| 134 } | 235 } |
| 135 | 236 |
| 136 mBoundForBackgroundPeriod = nextBound; | 237 mBoundForBackgroundPeriod = nextBound; |
| 137 } | 238 } |
| 138 | 239 |
| 139 boolean isOomProtected() { | 240 boolean isOomProtected() { |
| 140 // When a process crashes, we can be queried about its oom status be fore or after the | 241 // When a process crashes, we can be queried about its oom status be fore or after the |
| 141 // connection is cleared. For the latter case, the oom status is sta shed in | 242 // connection is cleared. For the latter case, the oom status is sta shed in |
| 142 // mWasOomProtected. | 243 // mWasOomProtected. |
| 143 return mConnection != null | 244 return mConnection != null |
| 144 ? mConnection.isOomProtectedOrWasWhenDied() : mWasOomProtect ed; | 245 ? mConnection.isOomProtectedOrWasWhenDied() : mWasOomProtect ed; |
| 145 } | 246 } |
| 146 | 247 |
| 147 void clearConnection() { | 248 void clearConnection() { |
| 148 mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); | 249 mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); |
| 250 if (mModerateBindingPool != null) { | |
| 251 mModerateBindingPool.removeConnection(this); | |
| 252 } | |
| 149 mConnection = null; | 253 mConnection = null; |
| 150 } | 254 } |
| 151 | 255 |
| 152 /** @return true iff the reference to the connection is no longer held * / | 256 /** @return true iff the reference to the connection is no longer held * / |
| 153 @VisibleForTesting | 257 @VisibleForTesting |
| 154 boolean isConnectionCleared() { | 258 boolean isConnectionCleared() { |
| 155 return mConnection == null; | 259 return mConnection == null; |
| 156 } | 260 } |
| 157 } | 261 } |
| 158 | 262 |
| 159 // This can be manipulated on different threads, synchronize access on mMana gedConnections. | 263 // This can be manipulated on different threads, synchronize access on mMana gedConnections. |
| 160 private final SparseArray<ManagedConnection> mManagedConnections = | 264 private final SparseArray<ManagedConnection> mManagedConnections = |
| 161 new SparseArray<ManagedConnection>(); | 265 new SparseArray<ManagedConnection>(); |
| 162 | 266 |
| 163 // The connection that was most recently set as foreground (using setInForeg round()). This is | 267 // The connection that was most recently set as foreground (using setInForeg round()). This is |
| 164 // used to add additional binding on it when the embedder goes to background . On low-end, this | 268 // used to add additional binding on it when the embedder goes to background . On low-end, this |
| 165 // is also used to drop process bidnings when a new one is created, making s ure that only one | 269 // is also used to drop process bidnings when a new one is created, making s ure that only one |
| 166 // renderer process at a time is protected from oom killing. | 270 // renderer process at a time is protected from oom killing. |
| 167 private ManagedConnection mLastInForeground; | 271 private ManagedConnection mLastInForeground; |
| 168 | 272 |
| 169 // Synchronizes operations that access mLastInForeground: setInForeground() and | 273 // Synchronizes operations that access mLastInForeground: setInForeground() and |
| 170 // addNewConnection(). | 274 // addNewConnection(). |
| 171 private final Object mLastInForegroundLock = new Object(); | 275 private final Object mLastInForegroundLock = new Object(); |
| 172 | 276 |
| 173 // The connection bound with additional binding in onSentToBackground(). | 277 // The connection bound with additional binding in onSentToBackground(). |
| 174 private ManagedConnection mBoundForBackgroundPeriod; | 278 private ManagedConnection mBoundForBackgroundPeriod; |
| 175 | 279 |
| 280 // Whether this instance is used on testing. | |
| 281 private boolean mOnTesting; | |
|
Yaron
2015/06/17 02:56:12
final
Jaekyun Seok (inactive)
2015/06/17 04:35:46
Done.
| |
| 282 | |
| 176 /** | 283 /** |
| 177 * The constructor is private to hide parameters exposed for testing from th e regular consumer. | 284 * The constructor is private to hide parameters exposed for testing from th e regular consumer. |
| 178 * Use factory methods to create an instance. | 285 * Use factory methods to create an instance. |
| 179 */ | 286 */ |
| 180 private BindingManagerImpl(boolean isLowMemoryDevice, long removeStrongBindi ngDelay) { | 287 private BindingManagerImpl( |
| 288 boolean isLowMemoryDevice, long removeStrongBindingDelay, boolean on Testing) { | |
| 181 mIsLowMemoryDevice = isLowMemoryDevice; | 289 mIsLowMemoryDevice = isLowMemoryDevice; |
| 182 mRemoveStrongBindingDelay = removeStrongBindingDelay; | 290 mRemoveStrongBindingDelay = removeStrongBindingDelay; |
| 291 mOnTesting = onTesting; | |
| 183 } | 292 } |
| 184 | 293 |
| 185 public static BindingManagerImpl createBindingManager() { | 294 public static BindingManagerImpl createBindingManager() { |
| 186 return new BindingManagerImpl(SysUtils.isLowEndDevice(), | 295 return new BindingManagerImpl( |
| 187 DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS); | 296 SysUtils.isLowEndDevice(), DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLI S, false); |
| 188 } | 297 } |
| 189 | 298 |
| 190 /** | 299 /** |
| 191 * Creates a testing instance of BindingManager. Testing instance will have the unbinding delays | 300 * Creates a testing instance of BindingManager. Testing instance will have the unbinding delays |
| 192 * set to 0, so that the tests don't need to deal with actual waiting. | 301 * set to 0, so that the tests don't need to deal with actual waiting. |
| 193 * @param isLowEndDevice true iff the created instance should apply low-end binding policies | 302 * @param isLowEndDevice true iff the created instance should apply low-end binding policies |
| 194 */ | 303 */ |
| 195 public static BindingManagerImpl createBindingManagerForTesting(boolean isLo wEndDevice) { | 304 public static BindingManagerImpl createBindingManagerForTesting(boolean isLo wEndDevice) { |
| 196 return new BindingManagerImpl(isLowEndDevice, 0); | 305 return new BindingManagerImpl(isLowEndDevice, 0, true); |
| 197 } | 306 } |
| 198 | 307 |
| 199 @Override | 308 @Override |
| 200 public void addNewConnection(int pid, ChildProcessConnection connection) { | 309 public void addNewConnection(int pid, ChildProcessConnection connection) { |
| 201 // This will reset the previous entry for the pid in the unlikely event of the OS | 310 // This will reset the previous entry for the pid in the unlikely event of the OS |
| 202 // reusing renderer pids. | 311 // reusing renderer pids. |
| 203 synchronized (mManagedConnections) { | 312 synchronized (mManagedConnections) { |
| 204 mManagedConnections.put(pid, new ManagedConnection(connection)); | 313 mManagedConnections.put(pid, new ManagedConnection(connection)); |
| 205 } | 314 } |
| 206 } | 315 } |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 249 public void onSentToBackground() { | 358 public void onSentToBackground() { |
| 250 assert mBoundForBackgroundPeriod == null; | 359 assert mBoundForBackgroundPeriod == null; |
| 251 synchronized (mLastInForegroundLock) { | 360 synchronized (mLastInForegroundLock) { |
| 252 // mLastInForeground can be null at this point as the embedding appl ication could be | 361 // mLastInForeground can be null at this point as the embedding appl ication could be |
| 253 // used in foreground without spawning any renderers. | 362 // used in foreground without spawning any renderers. |
| 254 if (mLastInForeground != null) { | 363 if (mLastInForeground != null) { |
| 255 mLastInForeground.setBoundForBackgroundPeriod(true); | 364 mLastInForeground.setBoundForBackgroundPeriod(true); |
| 256 mBoundForBackgroundPeriod = mLastInForeground; | 365 mBoundForBackgroundPeriod = mLastInForeground; |
| 257 } | 366 } |
| 258 } | 367 } |
| 368 if (mModerateBindingPool != null) { | |
| 369 Log.i(TAG, "Release moderate connections: " + mModerateBindingPool.s ize()); | |
| 370 if (!mOnTesting) { | |
| 371 RecordHistogram.recordCountHistogram( | |
| 372 "Android.ModerateBindingCount", mModerateBindingPool.siz e()); | |
| 373 } | |
| 374 mModerateBindingPool.evictAll(); | |
| 375 } | |
| 259 } | 376 } |
| 260 | 377 |
| 261 @Override | 378 @Override |
| 262 public void onBroughtToForeground() { | 379 public void onBroughtToForeground() { |
| 263 if (mBoundForBackgroundPeriod != null) { | 380 if (mBoundForBackgroundPeriod != null) { |
| 264 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); | 381 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); |
| 265 mBoundForBackgroundPeriod = null; | 382 mBoundForBackgroundPeriod = null; |
| 266 } | 383 } |
| 267 } | 384 } |
| 268 | 385 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 287 if (managedConnection != null) managedConnection.clearConnection(); | 404 if (managedConnection != null) managedConnection.clearConnection(); |
| 288 } | 405 } |
| 289 | 406 |
| 290 /** @return true iff the connection reference is no longer held */ | 407 /** @return true iff the connection reference is no longer held */ |
| 291 @VisibleForTesting | 408 @VisibleForTesting |
| 292 public boolean isConnectionCleared(int pid) { | 409 public boolean isConnectionCleared(int pid) { |
| 293 synchronized (mManagedConnections) { | 410 synchronized (mManagedConnections) { |
| 294 return mManagedConnections.get(pid).isConnectionCleared(); | 411 return mManagedConnections.get(pid).isConnectionCleared(); |
| 295 } | 412 } |
| 296 } | 413 } |
| 414 | |
| 415 @Override | |
| 416 public void startModerateBindingManagement( | |
| 417 Context context, int maxSize, float lowReduceRatio, float highReduce Ratio) { | |
| 418 if (mIsLowMemoryDevice || mModerateBindingPool != null) return; | |
| 419 | |
| 420 Log.i(TAG, "Moderate binding enabled: maxSize=" + maxSize + " lowReduceR atio=" | |
| 421 + lowReduceRatio + " highReduceRatio=" + highReduceRatio ); | |
| 422 mModerateBindingPool = new ModerateBindingPool(maxSize, lowReduceRatio, highReduceRatio); | |
| 423 if (context != null) context.registerComponentCallbacks(mModerateBinding Pool); | |
| 424 } | |
| 297 } | 425 } |
| OLD | NEW |