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 2eaf20eb1a23a47564349189b4176f946254269f..8008d8f48f91e2db2acb142f046b7fe665b33837 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 |
| @@ -40,11 +40,14 @@ class BindingManagerImpl implements BindingManager { |
| private final long mRemoveStrongBindingDelay; |
| private final boolean mIsLowMemoryDevice; |
| - private static class ModerateBindingPool |
| - extends LruCache<Integer, ManagedConnection> implements ComponentCallbacks2 { |
| + interface LauncherThreadExecutor { |
| + void execute(Runnable runnable); |
| + void executeDelayed(Runnable runnable, long delayInMS); |
| + } |
| + |
| + private class ModerateBindingPool extends LruCache<Integer, ChildProcessConnection> { |
|
Yaron
2015/09/14 22:30:59
Can you add a brief comment on this? It should exp
Jaekyun Seok (inactive)
2015/09/14 23:44:11
Done.
|
| private final float mLowReduceRatio; |
| private final float mHighReduceRatio; |
| - private final Object mDelayedClearerLock = new Object(); |
| private Runnable mDelayedClearer; |
| private final Handler mHandler = new Handler(ThreadUtils.getUiThreadLooper()); |
| @@ -55,34 +58,9 @@ class BindingManagerImpl implements BindingManager { |
| mHighReduceRatio = highReduceRatio; |
| } |
| - @Override |
| - public void onTrimMemory(int level) { |
| - Log.i(TAG, "onTrimMemory: level=%d, size=%d", level, size()); |
| - if (size() > 0) { |
| - if (level <= TRIM_MEMORY_RUNNING_MODERATE) { |
| - reduce(mLowReduceRatio); |
| - } else if (level <= TRIM_MEMORY_RUNNING_LOW) { |
| - reduce(mHighReduceRatio); |
| - } else if (level == TRIM_MEMORY_UI_HIDDEN) { |
| - // This will be handled by |mDelayedClearer|. |
| - return; |
| - } else { |
| - evictAll(); |
| - } |
| - } |
| - } |
| - |
| - @Override |
| - public void onLowMemory() { |
| - Log.i(TAG, "onLowMemory: evict %d bindings", size()); |
| - evictAll(); |
| - } |
| - |
| - @Override |
| - public void onConfigurationChanged(Configuration configuration) {} |
| - |
| @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) |
| - private void reduce(float reduceRatio) { |
| + public void reduce(boolean useHighReduceRatio) { |
| + final float reduceRatio = useHighReduceRatio ? mHighReduceRatio : mLowReduceRatio; |
| int oldSize = size(); |
| int newSize = (int) (oldSize * (1f - reduceRatio)); |
| Log.i(TAG, "Reduce connections from %d to %d", oldSize, newSize); |
| @@ -94,7 +72,7 @@ class BindingManagerImpl implements BindingManager { |
| // Entries will be removed from the front because snapshot() returns ones ordered |
| // from least recently accessed to most recently accessed. |
| int count = 0; |
| - for (Map.Entry<Integer, ManagedConnection> entry : snapshot().entrySet()) { |
| + for (Map.Entry<Integer, ChildProcessConnection> entry : snapshot().entrySet()) { |
| remove(entry.getKey()); |
| ++count; |
| if (count == oldSize - newSize) break; |
| @@ -102,70 +80,84 @@ class BindingManagerImpl implements BindingManager { |
| } |
| } |
| - void addConnection(ManagedConnection managedConnection) { |
| - ChildProcessConnection connection = managedConnection.mConnection; |
| + public void addConnection(ChildProcessConnection connection) { |
| if (connection != null && connection.isInSandbox()) { |
| - managedConnection.addModerateBinding(); |
| + addModerateBinding(connection); |
| if (connection.isModerateBindingBound()) { |
| - put(connection.getServiceNumber(), managedConnection); |
| + put(connection.getServiceNumber(), connection); |
| } else { |
| remove(connection.getServiceNumber()); |
| } |
| } |
| } |
| - void removeConnection(ManagedConnection managedConnection) { |
| - ChildProcessConnection connection = managedConnection.mConnection; |
| + public void removeConnection(ChildProcessConnection connection) { |
| 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(); |
| - } |
| + protected void entryRemoved(boolean evicted, Integer key, ChildProcessConnection oldValue, |
| + ChildProcessConnection newValue) { |
| + if (oldValue != newValue) removeModerateBinding(oldValue); |
| } |
| - void onSentToBackground(final boolean onTesting) { |
| + public void onSentToBackground(final boolean onTesting) { |
| if (size() == 0) return; |
| - synchronized (mDelayedClearerLock) { |
| - mDelayedClearer = new Runnable() { |
| - @Override |
| - public void run() { |
| - synchronized (mDelayedClearerLock) { |
| + mDelayedClearer = new Runnable() { |
| + @Override |
| + public void run() { |
| + mLauncherThreadExecutor.execute(new Runnable() { |
| + @Override |
| + public void run() { |
| if (mDelayedClearer == null) return; |
| mDelayedClearer = null; |
| + |
| + Log.i(TAG, "Release moderate connections: %d", size()); |
| + if (!onTesting) { |
| + RecordHistogram.recordCountHistogram( |
| + "Android.ModerateBindingCount", size()); |
| + } |
| + evictAll(); |
| } |
| - Log.i(TAG, "Release moderate connections: %d", size()); |
| - if (!onTesting) { |
| - RecordHistogram.recordCountHistogram( |
| - "Android.ModerateBindingCount", size()); |
| - } |
| - evictAll(); |
| - } |
| - }; |
| - mHandler.postDelayed(mDelayedClearer, onTesting |
| - ? MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS_ON_TESTING |
| - : MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS); |
| - } |
| + }); |
| + } |
| + }; |
| + mHandler.postDelayed(mDelayedClearer, onTesting |
| + ? MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS_ON_TESTING |
| + : MODERATE_BINDING_POOL_CLEARER_DELAY_MILLIS); |
| } |
| - void onBroughtToForeground() { |
| - synchronized (mDelayedClearerLock) { |
| - if (mDelayedClearer == null) return; |
| - mHandler.removeCallbacks(mDelayedClearer); |
| - mDelayedClearer = null; |
| - } |
| + public void onBroughtToForeground() { |
| + if (mDelayedClearer == null) return; |
| + mHandler.removeCallbacks(mDelayedClearer); |
| + mDelayedClearer = null; |
| + } |
| + |
| + private void removeModerateBinding(ChildProcessConnection connection) { |
| + if (connection == null || !connection.isModerateBindingBound()) return; |
| + connection.removeModerateBinding(); |
| + } |
| + |
| + private void addModerateBinding(ChildProcessConnection connection) { |
| + if (connection == null) return; |
| + |
| + connection.addModerateBinding(); |
| } |
| } |
| - private final Object mModerateBindingPoolLock = new Object(); |
| private ModerateBindingPool mModerateBindingPool; |
| /** |
| + * Interface to listen to events related strong binding. |
| + */ |
| + interface StrongBindingEventListener { |
| + void onAdded(ChildProcessConnection managedConnection); |
| + void onRemoved(ChildProcessConnection connection, boolean wasBoundForBackgroundPeriod); |
| + } |
| + |
| + /** |
| * Wraps ChildProcessConnection keeping track of additional information needed to manage the |
| * bindings of the connection. The reference to ChildProcessConnection is cleared when the |
| * connection goes away, but ManagedConnection itself is kept (until overwritten by a new entry |
| @@ -173,7 +165,7 @@ class BindingManagerImpl implements BindingManager { |
| */ |
| private class ManagedConnection { |
| // Set in constructor, cleared in clearConnection(). |
| - private ChildProcessConnection mConnection; |
| + private volatile ChildProcessConnection mConnection; |
| // True iff there is a strong binding kept on the service because it is working in |
| // foreground. |
| @@ -186,6 +178,8 @@ class BindingManagerImpl implements BindingManager { |
| // When mConnection is cleared, oom binding status is stashed here. |
| private boolean mWasOomProtected; |
| + private StrongBindingEventListener mStrongBindingEventListener; |
| + |
| /** Removes the initial service binding. */ |
| private void removeInitialBinding() { |
| if (mConnection == null || !mConnection.isInitialBindingBound()) return; |
| @@ -194,88 +188,66 @@ class BindingManagerImpl implements BindingManager { |
| /** Adds a strong service binding. */ |
| private void addStrongBinding() { |
| - ChildProcessConnection connection = mConnection; |
| - if (connection == null) return; |
| + if (mConnection == null) return; |
| - connection.addStrongBinding(); |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| - } |
| - if (moderateBindingPool != null) moderateBindingPool.removeConnection(this); |
| + mConnection.addStrongBinding(); |
| + mStrongBindingEventListener.onAdded(mConnection); |
| } |
| /** Removes a strong service binding. */ |
| - private void removeStrongBinding(final boolean keepsAsModerate) { |
| - final ChildProcessConnection connection = mConnection; |
| + private void removeStrongBinding(final boolean wasBoundForBackgroundPeriod) { |
| // 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. |
| - if (connection == null || !connection.isStrongBindingBound()) return; |
| + if (mConnection == null || !mConnection.isStrongBindingBound()) return; |
| // This runnable performs the actual unbinding. It will be executed synchronously when |
| // on low-end devices and posted with a delay otherwise. |
| - Runnable doUnbind = new Runnable() { |
| + final Runnable doUnbind = new Runnable() { |
| @Override |
| public void run() { |
| - if (connection.isStrongBindingBound()) { |
| - connection.removeStrongBinding(); |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| - } |
| - if (moderateBindingPool != null && !connection.isStrongBindingBound() |
| - && keepsAsModerate) { |
| - moderateBindingPool.addConnection(ManagedConnection.this); |
| - } |
| - } |
| + if (mConnection == null || !mConnection.isStrongBindingBound()) return; |
| + mConnection.removeStrongBinding(); |
| + mStrongBindingEventListener.onRemoved(mConnection, wasBoundForBackgroundPeriod); |
| } |
| }; |
| if (mIsLowMemoryDevice) { |
| doUnbind.run(); |
| } else { |
| - ThreadUtils.postOnUiThreadDelayed(doUnbind, mRemoveStrongBindingDelay); |
| + mLauncherThreadExecutor.executeDelayed(new Runnable() { |
| + @Override |
| + public void run() { |
| + doUnbind.run(); |
| + } |
| + }, mRemoveStrongBindingDelay); |
| } |
| } |
| - /** 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. |
| */ |
| - private void dropBindings() { |
| + public void dropBindings() { |
| assert mIsLowMemoryDevice; |
| - ChildProcessConnection connection = mConnection; |
| - if (connection == null) return; |
| + if (mConnection == null) return; |
| - connection.dropOomBindings(); |
| + mConnection.dropOomBindings(); |
| } |
| - ManagedConnection(ChildProcessConnection connection) { |
| + ManagedConnection(ChildProcessConnection connection, |
| + StrongBindingEventListener strongBindingEventListener) { |
| mConnection = connection; |
| + mStrongBindingEventListener = strongBindingEventListener; |
| } |
| /** |
| * Sets the visibility of the service, adding or removing the strong binding as needed. |
| */ |
| - void setInForeground(boolean nextInForeground) { |
| + public void setInForeground(boolean nextInForeground) { |
| if (!mInForeground && nextInForeground) { |
| addStrongBinding(); |
| } else if (mInForeground && !nextInForeground) { |
| - removeStrongBinding(true); |
| + removeStrongBinding(false); |
| } |
| mInForeground = nextInForeground; |
| @@ -284,7 +256,7 @@ class BindingManagerImpl implements BindingManager { |
| /** |
| * Removes the initial binding. |
| */ |
| - void determinedVisibility() { |
| + public void determinedVisibility() { |
| removeInitialBinding(); |
| } |
| @@ -292,37 +264,39 @@ class BindingManagerImpl implements BindingManager { |
| * Sets or removes additional binding when the service is main service during the embedder |
| * background period. |
| */ |
| - void setBoundForBackgroundPeriod(boolean nextBound) { |
| + public void setBoundForBackgroundPeriod(boolean nextBound) { |
| if (!mBoundForBackgroundPeriod && nextBound) { |
| addStrongBinding(); |
| } else if (mBoundForBackgroundPeriod && !nextBound) { |
| - removeStrongBinding(false); |
| + removeStrongBinding(true); |
| } |
| mBoundForBackgroundPeriod = nextBound; |
| } |
| - boolean isOomProtected() { |
| + /** This may be called from any thread. */ |
| + public boolean isOomProtected() { |
| // When a process crashes, we can be queried about its oom status before or after the |
| // connection is cleared. For the latter case, the oom status is stashed in |
| // mWasOomProtected. |
| - return mConnection != null |
| - ? mConnection.isOomProtectedOrWasWhenDied() : mWasOomProtected; |
| + ChildProcessConnection connection = mConnection; |
| + if (connection == null) return mWasOomProtected; |
| + |
| + return connection.isOomProtectedOrWasWhenDied(); |
| } |
| - void clearConnection() { |
| + public void clearConnection() { |
| + if (mConnection == null) return; |
| mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| - } |
| - if (moderateBindingPool != null) moderateBindingPool.removeConnection(this); |
| mConnection = null; |
| } |
| - /** @return true iff the reference to the connection is no longer held */ |
| + /** |
| + * This may be called from any thread. |
| + * @return true iff the reference to the connection is no longer held |
| + */ |
| @VisibleForTesting |
| - boolean isConnectionCleared() { |
| + public boolean isConnectionCleared() { |
| return mConnection == null; |
| } |
| } |
| @@ -337,30 +311,47 @@ class BindingManagerImpl implements BindingManager { |
| // renderer process at a time is protected from oom killing. |
| private ManagedConnection mLastInForeground; |
| - // Synchronizes operations that access mLastInForeground: setInForeground() and |
| - // addNewConnection(). |
| - private final Object mLastInForegroundLock = new Object(); |
| - |
| // The connection bound with additional binding in onSentToBackground(). |
| private ManagedConnection mBoundForBackgroundPeriod; |
| // Whether this instance is used on testing. |
| private final boolean mOnTesting; |
| + // Executor that executes a task on LauncherThread. |
| + private final LauncherThreadExecutor mLauncherThreadExecutor; |
| + |
| + private final Object mThreadIdLock = new Object(); |
| + private Long mThreadId; |
| + |
| + void checkCalledOnProcessLauncherThread() { |
| + if (mOnTesting) return; |
| + |
| + synchronized (mThreadIdLock) { |
| + long currentThreadId = Thread.currentThread().getId(); |
| + if (mThreadId == null) { |
| + mThreadId = currentThreadId; |
| + } else if (mThreadId != currentThreadId) { |
| + throw new IllegalStateException( |
| + "Method is not called on valid thread: " + Thread.currentThread()); |
| + } |
| + } |
| + } |
| + |
| /** |
| * 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, boolean onTesting) { |
| + private BindingManagerImpl(boolean isLowMemoryDevice, long removeStrongBindingDelay, |
| + boolean onTesting, LauncherThreadExecutor executor) { |
| mIsLowMemoryDevice = isLowMemoryDevice; |
| mRemoveStrongBindingDelay = removeStrongBindingDelay; |
| mOnTesting = onTesting; |
| + mLauncherThreadExecutor = executor; |
| } |
| - public static BindingManagerImpl createBindingManager() { |
| + public static BindingManagerImpl createBindingManager(LauncherThreadExecutor executor) { |
| return new BindingManagerImpl( |
| - SysUtils.isLowEndDevice(), DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS, false); |
| + SysUtils.isLowEndDevice(), DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS, false, executor); |
| } |
| /** |
| @@ -369,20 +360,56 @@ 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, true); |
| + LauncherThreadExecutor executor = new LauncherThreadExecutor() { |
| + @Override |
| + public void execute(Runnable runnable) { |
| + ThreadUtils.runOnUiThread(runnable); |
| + } |
| + |
| + @Override |
| + public void executeDelayed(Runnable runnable, long delayInMS) { |
| + ThreadUtils.postOnUiThreadDelayed(runnable, delayInMS); |
| + } |
| + }; |
| + return new BindingManagerImpl(isLowEndDevice, 0, true, executor); |
| } |
| @Override |
| public void addNewConnection(int pid, ChildProcessConnection connection) { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| // This will reset the previous entry for the pid in the unlikely event of the OS |
| // reusing renderer pids. |
| synchronized (mManagedConnections) { |
| - mManagedConnections.put(pid, new ManagedConnection(connection)); |
| + StrongBindingEventListener listener = new StrongBindingEventListener() { |
| + @Override |
| + public void onAdded(ChildProcessConnection connection) { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| + if (mModerateBindingPool != null) { |
| + mModerateBindingPool.removeConnection(connection); |
| + } |
| + } |
| + |
| + @Override |
| + public void onRemoved( |
| + ChildProcessConnection connection, boolean wasBoundForBackgroundPeriod) { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| + if (mModerateBindingPool != null && !connection.isStrongBindingBound() |
| + && !wasBoundForBackgroundPeriod) { |
| + mModerateBindingPool.addConnection(connection); |
| + } |
| + } |
| + }; |
| + mManagedConnections.put(pid, new ManagedConnection(connection, listener)); |
| } |
| } |
| @Override |
| public void setInForeground(int pid, boolean inForeground) { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| ManagedConnection managedConnection; |
| synchronized (mManagedConnections) { |
| managedConnection = mManagedConnections.get(pid); |
| @@ -393,19 +420,19 @@ class BindingManagerImpl implements BindingManager { |
| return; |
| } |
| - synchronized (mLastInForegroundLock) { |
| - if (inForeground && mIsLowMemoryDevice && mLastInForeground != null |
| - && mLastInForeground != managedConnection) { |
| - mLastInForeground.dropBindings(); |
| - } |
| - |
| - managedConnection.setInForeground(inForeground); |
| - if (inForeground) mLastInForeground = managedConnection; |
| + if (inForeground && mIsLowMemoryDevice && mLastInForeground != null |
| + && mLastInForeground != managedConnection) { |
| + mLastInForeground.dropBindings(); |
| } |
| + |
| + managedConnection.setInForeground(inForeground); |
| + if (inForeground) mLastInForeground = managedConnection; |
| } |
| @Override |
| public void determinedVisibility(int pid) { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| ManagedConnection managedConnection; |
| synchronized (mManagedConnections) { |
| managedConnection = mManagedConnections.get(pid); |
| @@ -422,35 +449,32 @@ class BindingManagerImpl implements BindingManager { |
| @Override |
| public void onSentToBackground() { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| assert mBoundForBackgroundPeriod == null; |
| - synchronized (mLastInForegroundLock) { |
| - // mLastInForeground can be null at this point as the embedding application could be |
| - // used in foreground without spawning any renderers. |
| - if (mLastInForeground != null) { |
| - mLastInForeground.setBoundForBackgroundPeriod(true); |
| - mBoundForBackgroundPeriod = mLastInForeground; |
| - } |
| - } |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| + |
| + // mLastInForeground can be null at this point as the embedding application could be |
| + // used in foreground without spawning any renderers. |
| + if (mLastInForeground != null) { |
| + mLastInForeground.setBoundForBackgroundPeriod(true); |
| + mBoundForBackgroundPeriod = mLastInForeground; |
| } |
| - if (moderateBindingPool != null) moderateBindingPool.onSentToBackground(mOnTesting); |
| + |
| + if (mModerateBindingPool != null) mModerateBindingPool.onSentToBackground(mOnTesting); |
| } |
| @Override |
| public void onBroughtToForeground() { |
| + checkCalledOnProcessLauncherThread(); |
| + |
| if (mBoundForBackgroundPeriod != null) { |
| mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); |
| mBoundForBackgroundPeriod = null; |
| } |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| - } |
| - if (moderateBindingPool != null) moderateBindingPool.onBroughtToForeground(); |
| + if (mModerateBindingPool != null) mModerateBindingPool.onBroughtToForeground(); |
| } |
| + /** This may be called from any thread. */ |
| @Override |
| public boolean isOomProtected(int pid) { |
| // In the unlikely event of the OS reusing renderer pid, the call will refer to the most |
| @@ -464,45 +488,94 @@ class BindingManagerImpl implements BindingManager { |
| } |
| @Override |
| - public void clearConnection(int pid) { |
| + public void clearConnection(final int pid) { |
|
Yaron
2015/09/14 22:30:59
why is this final now?
Jaekyun Seok (inactive)
2015/09/14 23:44:11
Removed.
|
| + checkCalledOnProcessLauncherThread(); |
| + |
| ManagedConnection managedConnection; |
| synchronized (mManagedConnections) { |
| managedConnection = mManagedConnections.get(pid); |
| } |
| - if (managedConnection != null) managedConnection.clearConnection(); |
| + // TODO: we need to find a way to clean up dead connections in mManagedConnections. |
| + if (managedConnection != null) { |
| + if (mModerateBindingPool != null) { |
| + mModerateBindingPool.removeConnection(managedConnection.mConnection); |
| + } |
| + managedConnection.clearConnection(); |
| + } |
| } |
| - /** @return true iff the connection reference is no longer held */ |
| + /** |
| + * This may be called from any thread. |
| + * @return true iff the connection reference is no longer held |
| + */ |
| @VisibleForTesting |
| public boolean isConnectionCleared(int pid) { |
| synchronized (mManagedConnections) { |
| - return mManagedConnections.get(pid).isConnectionCleared(); |
| + ManagedConnection managedConnection = mManagedConnections.get(pid); |
| + return managedConnection == null || managedConnection.isConnectionCleared(); |
| } |
| } |
| @Override |
| public void startModerateBindingManagement( |
| Context context, int maxSize, float lowReduceRatio, float highReduceRatio) { |
| - synchronized (mModerateBindingPoolLock) { |
| - if (mIsLowMemoryDevice || mModerateBindingPool != null) return; |
| + checkCalledOnProcessLauncherThread(); |
| + |
| + if (mIsLowMemoryDevice || mModerateBindingPool != null) return; |
| + |
| + Log.i(TAG, "Moderate binding enabled: maxSize=%d lowReduceRatio=%f highReduceRatio=%f", |
| + maxSize, lowReduceRatio, highReduceRatio); |
| + mModerateBindingPool = new ModerateBindingPool(maxSize, lowReduceRatio, highReduceRatio); |
| + if (context != null) { |
| + context.registerComponentCallbacks(new ComponentCallbacks2() { |
| + @Override |
| + public void onTrimMemory(final int level) { |
| + mLauncherThreadExecutor.execute(new Runnable() { |
| + @Override |
| + public void run() { |
| + Log.i(TAG, "onTrimMemory: level=%d, size=%d", level, |
| + mModerateBindingPool.size()); |
| + if (mModerateBindingPool.size() > 0) { |
| + if (level <= TRIM_MEMORY_RUNNING_MODERATE) { |
| + mModerateBindingPool.reduce(false); |
| + } else if (level <= TRIM_MEMORY_RUNNING_LOW) { |
| + mModerateBindingPool.reduce(true); |
| + } else if (level == TRIM_MEMORY_UI_HIDDEN) { |
| + // This will be handled by onSentToBackground(). |
| + return; |
| + } else { |
| + mModerateBindingPool.evictAll(); |
| + } |
| + } |
| + } |
| + }); |
| + } |
| - Log.i(TAG, "Moderate binding enabled: maxSize=%d lowReduceRatio=%f highReduceRatio=%f", |
| - maxSize, lowReduceRatio, highReduceRatio); |
| - mModerateBindingPool = |
| - new ModerateBindingPool(maxSize, lowReduceRatio, highReduceRatio); |
| - if (context != null) context.registerComponentCallbacks(mModerateBindingPool); |
| + @Override |
| + public void onLowMemory() { |
| + mLauncherThreadExecutor.execute(new Runnable() { |
| + @Override |
| + public void run() { |
| + Log.i(TAG, "onLowMemory: evict %d bindings", |
| + mModerateBindingPool.size()); |
| + mModerateBindingPool.evictAll(); |
| + } |
| + }); |
| + } |
| + |
| + @Override |
| + public void onConfigurationChanged(Configuration configuration) {} |
| + }); |
| } |
| } |
| @Override |
| public void releaseAllModerateBindings() { |
| - ModerateBindingPool moderateBindingPool; |
| - synchronized (mModerateBindingPoolLock) { |
| - moderateBindingPool = mModerateBindingPool; |
| - } |
| - if (moderateBindingPool != null) { |
| - Log.i(TAG, "Release all moderate bindings: %d", moderateBindingPool.size()); |
| - moderateBindingPool.evictAll(); |
| + checkCalledOnProcessLauncherThread(); |
| + |
| + if (mModerateBindingPool != null) { |
| + Log.i(TAG, "Release all moderate bindings: %d", mModerateBindingPool.size()); |
| + mModerateBindingPool.evictAll(); |
| } |
| } |
| } |