| 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.base.process_launcher; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.content.ComponentCallbacks2; | 8 import android.content.ComponentCallbacks2; |
| 9 import android.content.Context; | 9 import android.content.Context; |
| 10 import android.content.res.Configuration; | 10 import android.content.res.Configuration; |
| 11 import android.os.Build; | 11 import android.os.Build; |
| 12 import android.os.Handler; |
| 13 import android.os.Looper; |
| 12 import android.util.LruCache; | 14 import android.util.LruCache; |
| 13 import android.util.SparseArray; | 15 import android.util.SparseArray; |
| 14 | 16 |
| 15 import org.chromium.base.Log; | 17 import org.chromium.base.Log; |
| 16 import org.chromium.base.SysUtils; | 18 import org.chromium.base.SysUtils; |
| 17 import org.chromium.base.ThreadUtils; | 19 import org.chromium.base.ThreadUtils; |
| 18 import org.chromium.base.VisibleForTesting; | 20 import org.chromium.base.VisibleForTesting; |
| 19 import org.chromium.base.metrics.RecordHistogram; | 21 import org.chromium.base.metrics.RecordHistogram; |
| 20 | 22 |
| 21 import java.util.Map; | 23 import java.util.Map; |
| 22 | 24 |
| 23 /** | 25 /** |
| 24 * Manages oom bindings used to bound child services. | 26 * Manages oom bindings used to bound child services. |
| 25 * This object must only be accessed from the launcher thread. | 27 * This object must only be accessed from the launcher thread. |
| 26 */ | 28 */ |
| 27 class BindingManagerImpl implements BindingManager { | 29 public class BindingManagerImpl implements BindingManager { |
| 28 private static final String TAG = "cr.BindingManager"; | 30 private static final String TAG = "cr_BindingManager"; |
| 29 | 31 |
| 30 // Low reduce ratio of moderate binding. | 32 // Low reduce ratio of moderate binding. |
| 31 private static final float MODERATE_BINDING_LOW_REDUCE_RATIO = 0.25f; | 33 private static final float MODERATE_BINDING_LOW_REDUCE_RATIO = 0.25f; |
| 32 // High reduce ratio of moderate binding. | 34 // High reduce ratio of moderate binding. |
| 33 private static final float MODERATE_BINDING_HIGH_REDUCE_RATIO = 0.5f; | 35 private static final float MODERATE_BINDING_HIGH_REDUCE_RATIO = 0.5f; |
| 34 | 36 |
| 35 // Delay of 1 second used when removing temporary strong binding of a proces
s (only on | 37 // Delay of 1 second used when removing temporary strong binding of a proces
s (only on |
| 36 // non-low-memory devices). | 38 // non-low-memory devices). |
| 37 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000; | 39 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000; |
| 38 | 40 |
| 39 // Delays used when clearing moderate binding pool when onSentToBackground h
appens. | 41 // Delays used when clearing moderate binding pool when onSentToBackground h
appens. |
| 40 private static final long MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS = 10 *
1000; | 42 private static final long MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS = 10 *
1000; |
| 41 | 43 |
| 44 // The handler for the thread this BindingManager should be accessed on. |
| 45 private final Handler mHandler; |
| 46 |
| 42 // These fields allow to override the parameters for testing - see | 47 // These fields allow to override the parameters for testing - see |
| 43 // createBindingManagerForTesting(). | 48 // createBindingManagerForTesting(). |
| 44 private final boolean mIsLowMemoryDevice; | 49 private final boolean mIsLowMemoryDevice; |
| 45 | 50 |
| 46 private static class ModerateBindingPool | 51 private class ModerateBindingPool |
| 47 extends LruCache<Integer, ManagedConnection> implements ComponentCal
lbacks2 { | 52 extends LruCache<Integer, ManagedConnection> implements ComponentCal
lbacks2 { |
| 48 private Runnable mDelayedClearer; | 53 private Runnable mDelayedClearer; |
| 49 | 54 |
| 50 public ModerateBindingPool(int maxSize) { | 55 public ModerateBindingPool(int maxSize) { |
| 51 super(maxSize); | 56 super(maxSize); |
| 52 } | 57 } |
| 53 | 58 |
| 54 @Override | 59 @Override |
| 55 public void onTrimMemory(final int level) { | 60 public void onTrimMemory(final int level) { |
| 56 ThreadUtils.assertOnUiThread(); | 61 ThreadUtils.assertOnUiThread(); |
| 57 LauncherThread.post(new Runnable() { | 62 mHandler.post(new Runnable() { |
| 58 @Override | 63 @Override |
| 59 public void run() { | 64 public void run() { |
| 60 Log.i(TAG, "onTrimMemory: level=%d, size=%d", level, size())
; | 65 Log.i(TAG, "onTrimMemory: level=%d, size=%d", level, size())
; |
| 61 if (size() <= 0) { | 66 if (size() <= 0) { |
| 62 return; | 67 return; |
| 63 } | 68 } |
| 64 if (level <= TRIM_MEMORY_RUNNING_MODERATE) { | 69 if (level <= TRIM_MEMORY_RUNNING_MODERATE) { |
| 65 reduce(MODERATE_BINDING_LOW_REDUCE_RATIO); | 70 reduce(MODERATE_BINDING_LOW_REDUCE_RATIO); |
| 66 } else if (level <= TRIM_MEMORY_RUNNING_LOW) { | 71 } else if (level <= TRIM_MEMORY_RUNNING_LOW) { |
| 67 reduce(MODERATE_BINDING_HIGH_REDUCE_RATIO); | 72 reduce(MODERATE_BINDING_HIGH_REDUCE_RATIO); |
| 68 } else if (level == TRIM_MEMORY_UI_HIDDEN) { | 73 } else if (level == TRIM_MEMORY_UI_HIDDEN) { |
| 69 // This will be handled by |mDelayedClearer|. | 74 // This will be handled by |mDelayedClearer|. |
| 70 return; | 75 return; |
| 71 } else { | 76 } else { |
| 72 evictAll(); | 77 evictAll(); |
| 73 } | 78 } |
| 74 } | 79 } |
| 75 }); | 80 }); |
| 76 } | 81 } |
| 77 | 82 |
| 78 @Override | 83 @Override |
| 79 public void onLowMemory() { | 84 public void onLowMemory() { |
| 80 ThreadUtils.assertOnUiThread(); | 85 ThreadUtils.assertOnUiThread(); |
| 81 LauncherThread.post(new Runnable() { | 86 mHandler.post(new Runnable() { |
| 82 @Override | 87 @Override |
| 83 public void run() { | 88 public void run() { |
| 84 Log.i(TAG, "onLowMemory: evict %d bindings", size()); | 89 Log.i(TAG, "onLowMemory: evict %d bindings", size()); |
| 85 evictAll(); | 90 evictAll(); |
| 86 } | 91 } |
| 87 }); | 92 }); |
| 88 } | 93 } |
| 89 | 94 |
| 90 @Override | 95 @Override |
| 91 public void onConfigurationChanged(Configuration configuration) {} | 96 public void onConfigurationChanged(Configuration configuration) {} |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 if (mDelayedClearer == null) return; | 151 if (mDelayedClearer == null) return; |
| 147 mDelayedClearer = null; | 152 mDelayedClearer = null; |
| 148 Log.i(TAG, "Release moderate connections: %d", size()); | 153 Log.i(TAG, "Release moderate connections: %d", size()); |
| 149 if (!onTesting) { | 154 if (!onTesting) { |
| 150 RecordHistogram.recordCountHistogram( | 155 RecordHistogram.recordCountHistogram( |
| 151 "Android.ModerateBindingCount", size()); | 156 "Android.ModerateBindingCount", size()); |
| 152 } | 157 } |
| 153 evictAll(); | 158 evictAll(); |
| 154 } | 159 } |
| 155 }; | 160 }; |
| 156 LauncherThread.postDelayed(mDelayedClearer, MODERATE_BINDING_POOL_CL
EARER_DELAY_MILLIS); | 161 mHandler.postDelayed(mDelayedClearer, MODERATE_BINDING_POOL_CLEARER_
DELAY_MILLIS); |
| 157 } | 162 } |
| 158 | 163 |
| 159 void onBroughtToForeground() { | 164 void onBroughtToForeground() { |
| 160 if (mDelayedClearer != null) { | 165 if (mDelayedClearer != null) { |
| 161 LauncherThread.removeCallbacks(mDelayedClearer); | 166 mHandler.removeCallbacks(mDelayedClearer); |
| 162 mDelayedClearer = null; | 167 mDelayedClearer = null; |
| 163 } | 168 } |
| 164 } | 169 } |
| 165 } | 170 } |
| 166 | 171 |
| 167 private ModerateBindingPool mModerateBindingPool; | 172 private ModerateBindingPool mModerateBindingPool; |
| 168 | 173 |
| 169 /** | 174 /** |
| 170 * Wraps ManagedChildProcessConnection keeping track of additional informati
on needed to manage | 175 * Wraps ManagedChildProcessConnection keeping track of additional informati
on needed to manage |
| 171 * the bindings of the connection. The reference to ManagedChildProcessConne
ction is cleared | 176 * the bindings of the connection. The reference to ManagedChildProcessConne
ction is cleared |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 if (keepAsModerate) { | 228 if (keepAsModerate) { |
| 224 addConnectionToModerateBindingPool(connection); | 229 addConnectionToModerateBindingPool(connection); |
| 225 } | 230 } |
| 226 } | 231 } |
| 227 } | 232 } |
| 228 }; | 233 }; |
| 229 | 234 |
| 230 if (mIsLowMemoryDevice) { | 235 if (mIsLowMemoryDevice) { |
| 231 doUnbind.run(); | 236 doUnbind.run(); |
| 232 } else { | 237 } else { |
| 233 LauncherThread.postDelayed(doUnbind, DETACH_AS_ACTIVE_HIGH_END_D
ELAY_MILLIS); | 238 mHandler.postDelayed(doUnbind, DETACH_AS_ACTIVE_HIGH_END_DELAY_M
ILLIS); |
| 234 } | 239 } |
| 235 } | 240 } |
| 236 | 241 |
| 237 /** | 242 /** |
| 238 * Adds connection to the moderate binding pool. No-op if the connection
has a strong | 243 * Adds connection to the moderate binding pool. No-op if the connection
has a strong |
| 239 * binding. | 244 * binding. |
| 240 * @param connection The ChildProcessConnection to add to the moderate b
inding pool. | 245 * @param connection The ChildProcessConnection to add to the moderate b
inding pool. |
| 241 */ | 246 */ |
| 242 private void addConnectionToModerateBindingPool(ManagedChildProcessConne
ction connection) { | 247 private void addConnectionToModerateBindingPool(ManagedChildProcessConne
ction connection) { |
| 243 if (mModerateBindingPool != null && !connection.isStrongBindingBound
()) { | 248 if (mModerateBindingPool != null && !connection.isStrongBindingBound
()) { |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 // The connection bound with additional binding in onSentToBackground(). | 336 // The connection bound with additional binding in onSentToBackground(). |
| 332 private ManagedConnection mBoundForBackgroundPeriod; | 337 private ManagedConnection mBoundForBackgroundPeriod; |
| 333 | 338 |
| 334 // Whether this instance is used on testing. | 339 // Whether this instance is used on testing. |
| 335 private final boolean mOnTesting; | 340 private final boolean mOnTesting; |
| 336 | 341 |
| 337 /** | 342 /** |
| 338 * The constructor is private to hide parameters exposed for testing from th
e regular consumer. | 343 * The constructor is private to hide parameters exposed for testing from th
e regular consumer. |
| 339 * Use factory methods to create an instance. | 344 * Use factory methods to create an instance. |
| 340 */ | 345 */ |
| 341 private BindingManagerImpl(boolean isLowMemoryDevice, boolean onTesting) { | 346 private BindingManagerImpl(Handler handler, boolean isLowMemoryDevice, boole
an onTesting) { |
| 342 assert LauncherThread.runningOnLauncherThread(); | 347 mHandler = handler; |
| 348 checkOnValidThread(); |
| 343 mIsLowMemoryDevice = isLowMemoryDevice; | 349 mIsLowMemoryDevice = isLowMemoryDevice; |
| 344 mOnTesting = onTesting; | 350 mOnTesting = onTesting; |
| 345 } | 351 } |
| 346 | 352 |
| 347 public static BindingManagerImpl createBindingManager() { | 353 public static BindingManagerImpl createBindingManager(Handler handler) { |
| 348 assert LauncherThread.runningOnLauncherThread(); | 354 return new BindingManagerImpl(handler, SysUtils.isLowEndDevice(), false)
; |
| 349 return new BindingManagerImpl(SysUtils.isLowEndDevice(), false); | |
| 350 } | 355 } |
| 351 | 356 |
| 352 /** | 357 /** |
| 353 * Creates a testing instance of BindingManager. Testing instance will have
the unbinding delays | 358 * Creates a testing instance of BindingManager. Testing instance will have
the unbinding delays |
| 354 * set to 0, so that the tests don't need to deal with actual waiting. | 359 * set to 0, so that the tests don't need to deal with actual waiting. |
| 355 * @param isLowEndDevice true iff the created instance should apply low-end
binding policies | 360 * @param isLowEndDevice true iff the created instance should apply low-end
binding policies |
| 356 */ | 361 */ |
| 357 public static BindingManagerImpl createBindingManagerForTesting(boolean isLo
wEndDevice) { | 362 public static BindingManagerImpl createBindingManagerForTesting( |
| 358 assert LauncherThread.runningOnLauncherThread(); | 363 Handler handler, boolean isLowEndDevice) { |
| 359 return new BindingManagerImpl(isLowEndDevice, true); | 364 return new BindingManagerImpl(handler, isLowEndDevice, true); |
| 360 } | 365 } |
| 361 | 366 |
| 362 @Override | 367 @Override |
| 363 public void addNewConnection(int pid, ManagedChildProcessConnection connecti
on) { | 368 public void addNewConnection(int pid, ManagedChildProcessConnection connecti
on) { |
| 364 assert LauncherThread.runningOnLauncherThread(); | 369 checkOnValidThread(); |
| 365 // This will reset the previous entry for the pid in the unlikely event
of the OS | 370 // This will reset the previous entry for the pid in the unlikely event
of the OS |
| 366 // reusing renderer pids. | 371 // reusing renderer pids. |
| 367 mManagedConnections.put(pid, new ManagedConnection(connection)); | 372 mManagedConnections.put(pid, new ManagedConnection(connection)); |
| 368 } | 373 } |
| 369 | 374 |
| 370 @Override | 375 @Override |
| 371 public void setInForeground(int pid, boolean inForeground) { | 376 public void setInForeground(int pid, boolean inForeground) { |
| 372 assert LauncherThread.runningOnLauncherThread(); | 377 checkOnValidThread(); |
| 373 ManagedConnection managedConnection = mManagedConnections.get(pid); | 378 ManagedConnection managedConnection = mManagedConnections.get(pid); |
| 374 if (managedConnection == null) { | 379 if (managedConnection == null) { |
| 375 Log.w(TAG, "Cannot setInForeground() - never saw a connection for th
e pid: %d", pid); | 380 Log.w(TAG, "Cannot setInForeground() - never saw a connection for th
e pid: %d", pid); |
| 376 return; | 381 return; |
| 377 } | 382 } |
| 378 | 383 |
| 379 if (inForeground && mIsLowMemoryDevice && mLastInForeground != null | 384 if (inForeground && mIsLowMemoryDevice && mLastInForeground != null |
| 380 && mLastInForeground != managedConnection) { | 385 && mLastInForeground != managedConnection) { |
| 381 mLastInForeground.dropBindings(); | 386 mLastInForeground.dropBindings(); |
| 382 } | 387 } |
| 383 | 388 |
| 384 managedConnection.setInForeground(inForeground); | 389 managedConnection.setInForeground(inForeground); |
| 385 if (inForeground) mLastInForeground = managedConnection; | 390 if (inForeground) mLastInForeground = managedConnection; |
| 386 } | 391 } |
| 387 | 392 |
| 388 @Override | 393 @Override |
| 389 public void onDeterminedVisibility(int pid) { | 394 public void onDeterminedVisibility(int pid) { |
| 390 assert LauncherThread.runningOnLauncherThread(); | 395 checkOnValidThread(); |
| 391 ManagedConnection managedConnection = mManagedConnections.get(pid); | 396 ManagedConnection managedConnection = mManagedConnections.get(pid); |
| 392 if (managedConnection == null) { | 397 if (managedConnection == null) { |
| 393 Log.w(TAG, "Cannot call determinedVisibility() - never saw a connect
ion for the pid: " | 398 Log.w(TAG, |
| 394 + "%d", pid); | 399 "Cannot call determinedVisibility() - never saw a connection
for the pid: " |
| 400 + "%d", |
| 401 pid); |
| 395 return; | 402 return; |
| 396 } | 403 } |
| 397 | 404 |
| 398 managedConnection.onDeterminedVisibility(); | 405 managedConnection.onDeterminedVisibility(); |
| 399 } | 406 } |
| 400 | 407 |
| 401 @Override | 408 @Override |
| 402 public void onSentToBackground() { | 409 public void onSentToBackground() { |
| 403 assert LauncherThread.runningOnLauncherThread(); | 410 checkOnValidThread(); |
| 404 assert mBoundForBackgroundPeriod == null; | 411 assert mBoundForBackgroundPeriod == null; |
| 405 // mLastInForeground can be null at this point as the embedding applicat
ion could be | 412 // mLastInForeground can be null at this point as the embedding applicat
ion could be |
| 406 // used in foreground without spawning any renderers. | 413 // used in foreground without spawning any renderers. |
| 407 if (mLastInForeground != null) { | 414 if (mLastInForeground != null) { |
| 408 mLastInForeground.setBoundForBackgroundPeriod(true); | 415 mLastInForeground.setBoundForBackgroundPeriod(true); |
| 409 mBoundForBackgroundPeriod = mLastInForeground; | 416 mBoundForBackgroundPeriod = mLastInForeground; |
| 410 } | 417 } |
| 411 if (mModerateBindingPool != null) mModerateBindingPool.onSentToBackgroun
d(mOnTesting); | 418 if (mModerateBindingPool != null) mModerateBindingPool.onSentToBackgroun
d(mOnTesting); |
| 412 } | 419 } |
| 413 | 420 |
| 414 @Override | 421 @Override |
| 415 public void onBroughtToForeground() { | 422 public void onBroughtToForeground() { |
| 416 assert LauncherThread.runningOnLauncherThread(); | 423 checkOnValidThread(); |
| 417 if (mBoundForBackgroundPeriod != null) { | 424 if (mBoundForBackgroundPeriod != null) { |
| 418 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); | 425 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); |
| 419 mBoundForBackgroundPeriod = null; | 426 mBoundForBackgroundPeriod = null; |
| 420 } | 427 } |
| 421 if (mModerateBindingPool != null) mModerateBindingPool.onBroughtToForegr
ound(); | 428 if (mModerateBindingPool != null) mModerateBindingPool.onBroughtToForegr
ound(); |
| 422 } | 429 } |
| 423 | 430 |
| 424 @Override | 431 @Override |
| 425 public void removeConnection(int pid) { | 432 public void removeConnection(int pid) { |
| 426 assert LauncherThread.runningOnLauncherThread(); | 433 checkOnValidThread(); |
| 427 ManagedConnection managedConnection = mManagedConnections.get(pid); | 434 ManagedConnection managedConnection = mManagedConnections.get(pid); |
| 428 if (managedConnection != null) { | 435 if (managedConnection != null) { |
| 429 mManagedConnections.remove(pid); | 436 mManagedConnections.remove(pid); |
| 430 managedConnection.clearConnection(); | 437 managedConnection.clearConnection(); |
| 431 } | 438 } |
| 432 } | 439 } |
| 433 | 440 |
| 434 /** @return true iff the connection reference is no longer held */ | 441 /** @return true iff the connection reference is no longer held */ |
| 435 @VisibleForTesting | 442 @VisibleForTesting |
| 436 public boolean isConnectionCleared(int pid) { | 443 public boolean isConnectionCleared(int pid) { |
| 437 assert LauncherThread.runningOnLauncherThread(); | 444 checkOnValidThread(); |
| 438 return mManagedConnections.get(pid) == null; | 445 return mManagedConnections.get(pid) == null; |
| 439 } | 446 } |
| 440 | 447 |
| 441 @Override | 448 @Override |
| 442 public void startModerateBindingManagement(Context context, int maxSize) { | 449 public void startModerateBindingManagement(Context context, int maxSize) { |
| 443 assert LauncherThread.runningOnLauncherThread(); | 450 checkOnValidThread(); |
| 444 if (mIsLowMemoryDevice) return; | 451 if (mIsLowMemoryDevice) return; |
| 445 | 452 |
| 446 if (mModerateBindingPool == null) { | 453 if (mModerateBindingPool == null) { |
| 447 Log.i(TAG, "Moderate binding enabled: maxSize=%d", maxSize); | 454 Log.i(TAG, "Moderate binding enabled: maxSize=%d", maxSize); |
| 448 mModerateBindingPool = new ModerateBindingPool(maxSize); | 455 mModerateBindingPool = new ModerateBindingPool(maxSize); |
| 449 if (context != null) { | 456 if (context != null) { |
| 450 // Note that it is safe to call Context.registerComponentCallbac
ks from a background | 457 // Note that it is safe to call Context.registerComponentCallbac
ks from a background |
| 451 // thread. | 458 // thread. |
| 452 context.registerComponentCallbacks(mModerateBindingPool); | 459 context.registerComponentCallbacks(mModerateBindingPool); |
| 453 } | 460 } |
| 454 } | 461 } |
| 455 } | 462 } |
| 456 | 463 |
| 457 @Override | 464 @Override |
| 458 public void releaseAllModerateBindings() { | 465 public void releaseAllModerateBindings() { |
| 459 assert LauncherThread.runningOnLauncherThread(); | 466 checkOnValidThread(); |
| 460 if (mModerateBindingPool != null) { | 467 if (mModerateBindingPool != null) { |
| 461 Log.i(TAG, "Release all moderate bindings: %d", mModerateBindingPool
.size()); | 468 Log.i(TAG, "Release all moderate bindings: %d", mModerateBindingPool
.size()); |
| 462 mModerateBindingPool.evictAll(); | 469 mModerateBindingPool.evictAll(); |
| 463 } | 470 } |
| 464 } | 471 } |
| 472 |
| 473 /** Checks whether the caller is running on the thread specified at creation
time. */ |
| 474 private final void checkOnValidThread() { |
| 475 assert mHandler.getLooper() == Looper.myLooper(); |
| 476 } |
| 465 } | 477 } |
| OLD | NEW |