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 |