| 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..cda8dce86d962cf9a46dfd4f37c93c90924c3fd6 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,18 @@ 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);
|
| + }
|
| +
|
| + /**
|
| + * Manages moderate bindings. Note that all the public methods should be called from process
|
| + * launcher thread.
|
| + */
|
| + private class ModerateBindingPool extends LruCache<Integer, ChildProcessConnection> {
|
| 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 +62,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 +76,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 +84,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 +169,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 +182,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 +192,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 +260,7 @@ class BindingManagerImpl implements BindingManager {
|
| /**
|
| * Removes the initial binding.
|
| */
|
| - void determinedVisibility() {
|
| + public void determinedVisibility() {
|
| removeInitialBinding();
|
| }
|
|
|
| @@ -292,37 +268,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 +315,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 +364,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 +424,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 +453,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
|
| @@ -465,44 +493,93 @@ class BindingManagerImpl implements BindingManager {
|
|
|
| @Override
|
| public void clearConnection(int pid) {
|
| + 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();
|
| }
|
| }
|
| }
|
|
|