OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.content.browser; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.os.Bundle; |
| 9 |
| 10 import org.chromium.base.Log; |
| 11 import org.chromium.base.VisibleForTesting; |
| 12 import org.chromium.base.process_launcher.ChildProcessCreationParams; |
| 13 |
| 14 import javax.annotation.concurrent.GuardedBy; |
| 15 |
| 16 /** |
| 17 * ManagedChildProcessConnection is a connection to a child service that can hol
d several bindings |
| 18 * to the service so it can be more or less agressively protected against OOM. |
| 19 */ |
| 20 public class ManagedChildProcessConnection extends BaseChildProcessConnection { |
| 21 private static final String TAG = "ManChildProcessConn"; |
| 22 |
| 23 public static final Factory FACTORY = new BaseChildProcessConnection.Factory
() { |
| 24 @Override |
| 25 public BaseChildProcessConnection create(Context context, int number, bo
olean sandboxed, |
| 26 DeathCallback deathCallback, String serviceClassName, |
| 27 Bundle childProcessCommonParameters, ChildProcessCreationParams
creationParams) { |
| 28 return new ManagedChildProcessConnection(context, number, sandboxed,
deathCallback, |
| 29 serviceClassName, childProcessCommonParameters, creationPara
ms); |
| 30 } |
| 31 }; |
| 32 |
| 33 // Synchronization: While most internal flow occurs on the UI thread, the pu
blic API |
| 34 // (specifically start and stop) may be called from any thread, hence all en
try point methods |
| 35 // into the class are synchronized on the lock to protect access to these me
mbers. |
| 36 private final Object mBindingLock = new Object(); |
| 37 |
| 38 // Initial binding protects the newly spawned process from being killed befo
re it is put to use, |
| 39 // it is maintained between calls to start() and removeInitialBinding(). |
| 40 @GuardedBy("mBindingLock") |
| 41 private final ChildServiceConnection mInitialBinding; |
| 42 |
| 43 // Strong binding will make the service priority equal to the priority of th
e activity. We want |
| 44 // the OS to be able to kill background renderers as it kills other backgrou
nd apps, so strong |
| 45 // bindings are maintained only for services that are active at the moment (
between |
| 46 // addStrongBinding() and removeStrongBinding()). |
| 47 @GuardedBy("mBindingLock") |
| 48 private final ChildServiceConnection mStrongBinding; |
| 49 |
| 50 // Low priority binding maintained in the entire lifetime of the connection,
i.e. between calls |
| 51 // to start() and stop(). |
| 52 @GuardedBy("mBindingLock") |
| 53 private final ChildServiceConnection mWaivedBinding; |
| 54 |
| 55 // Incremented on addStrongBinding(), decremented on removeStrongBinding(). |
| 56 @GuardedBy("mBindingLock") |
| 57 private int mStrongBindingCount; |
| 58 |
| 59 // Moderate binding will make the service priority equal to the priority of
a visible process |
| 60 // while the app is in the foreground. It will stay bound only while the app
is in the |
| 61 // foreground to protect a background process from the system out-of-memory
killer. |
| 62 @GuardedBy("mBindingLock") |
| 63 private final ChildServiceConnection mModerateBinding; |
| 64 |
| 65 @GuardedBy("mBindingLock") |
| 66 private boolean mWasOomProtectedOnUnbind; |
| 67 |
| 68 @VisibleForTesting |
| 69 ManagedChildProcessConnection(Context context, int number, boolean sandboxed
, |
| 70 DeathCallback deathCallback, String serviceClassName, |
| 71 Bundle childProcessCommonParameters, ChildProcessCreationParams crea
tionParams) { |
| 72 super(context, number, sandboxed, deathCallback, serviceClassName, |
| 73 childProcessCommonParameters, creationParams); |
| 74 |
| 75 int initialFlags = Context.BIND_AUTO_CREATE; |
| 76 int extraBindFlags = shouldBindAsExportedService() ? Context.BIND_EXTERN
AL_SERVICE : 0; |
| 77 |
| 78 synchronized (mBindingLock) { |
| 79 mInitialBinding = createServiceConnection(initialFlags | extraBindFl
ags); |
| 80 mStrongBinding = createServiceConnection( |
| 81 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBin
dFlags); |
| 82 mWaivedBinding = createServiceConnection( |
| 83 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | ext
raBindFlags); |
| 84 mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE
| extraBindFlags); |
| 85 } |
| 86 } |
| 87 |
| 88 @Override |
| 89 protected boolean bind() { |
| 90 synchronized (mBindingLock) { |
| 91 if (!mInitialBinding.bind()) { |
| 92 return false; |
| 93 } |
| 94 mWaivedBinding.bind(); |
| 95 } |
| 96 return true; |
| 97 } |
| 98 |
| 99 @Override |
| 100 public void unbind() { |
| 101 synchronized (mBindingLock) { |
| 102 if (!isBound()) { |
| 103 return; |
| 104 } |
| 105 mWasOomProtectedOnUnbind = isCurrentlyOomProtected(); |
| 106 mInitialBinding.unbind(); |
| 107 mStrongBinding.unbind(); |
| 108 mWaivedBinding.unbind(); |
| 109 mModerateBinding.unbind(); |
| 110 mStrongBindingCount = 0; |
| 111 } |
| 112 } |
| 113 |
| 114 @GuardedBy("mBindingLock") |
| 115 private boolean isBound() { |
| 116 return mInitialBinding.isBound() || mStrongBinding.isBound() || mWaivedB
inding.isBound() |
| 117 || mModerateBinding.isBound(); |
| 118 } |
| 119 |
| 120 public boolean isInitialBindingBound() { |
| 121 synchronized (mBindingLock) { |
| 122 return mInitialBinding.isBound(); |
| 123 } |
| 124 } |
| 125 |
| 126 public boolean isStrongBindingBound() { |
| 127 synchronized (mBindingLock) { |
| 128 return mStrongBinding.isBound(); |
| 129 } |
| 130 } |
| 131 |
| 132 public void removeInitialBinding() { |
| 133 synchronized (mBindingLock) { |
| 134 mInitialBinding.unbind(); |
| 135 } |
| 136 } |
| 137 |
| 138 public boolean isOomProtectedOrWasWhenDied() { |
| 139 // Call isConnected() outside of the synchronized block or we could dead
lock. |
| 140 final boolean isConnected = isConnected(); |
| 141 synchronized (mBindingLock) { |
| 142 if (isConnected) { |
| 143 return isCurrentlyOomProtected(); |
| 144 } |
| 145 return mWasOomProtectedOnUnbind; |
| 146 } |
| 147 } |
| 148 |
| 149 @GuardedBy("mBindingLock") |
| 150 private boolean isCurrentlyOomProtected() { |
| 151 return mInitialBinding.isBound() || mStrongBinding.isBound(); |
| 152 } |
| 153 |
| 154 public void dropOomBindings() { |
| 155 synchronized (mBindingLock) { |
| 156 mInitialBinding.unbind(); |
| 157 |
| 158 mStrongBindingCount = 0; |
| 159 mStrongBinding.unbind(); |
| 160 |
| 161 mModerateBinding.unbind(); |
| 162 } |
| 163 } |
| 164 |
| 165 public void addStrongBinding() { |
| 166 // Call isConnected() outside of the synchronized block or we could dead
lock. |
| 167 final boolean isConnected = isConnected(); |
| 168 synchronized (mBindingLock) { |
| 169 if (!isConnected) { |
| 170 Log.w(TAG, "The connection is not bound for %d", getPid()); |
| 171 return; |
| 172 } |
| 173 if (mStrongBindingCount == 0) { |
| 174 mStrongBinding.bind(); |
| 175 } |
| 176 mStrongBindingCount++; |
| 177 } |
| 178 } |
| 179 |
| 180 public void removeStrongBinding() { |
| 181 // Call isConnected() outside of the synchronized block or we could dead
lock. |
| 182 final boolean isConnected = isConnected(); |
| 183 synchronized (mBindingLock) { |
| 184 if (!isConnected) { |
| 185 Log.w(TAG, "The connection is not bound for %d", getPid()); |
| 186 return; |
| 187 } |
| 188 assert mStrongBindingCount > 0; |
| 189 mStrongBindingCount--; |
| 190 if (mStrongBindingCount == 0) { |
| 191 mStrongBinding.unbind(); |
| 192 } |
| 193 } |
| 194 } |
| 195 |
| 196 public boolean isModerateBindingBound() { |
| 197 synchronized (mBindingLock) { |
| 198 return mModerateBinding.isBound(); |
| 199 } |
| 200 } |
| 201 |
| 202 public void addModerateBinding() { |
| 203 // Call isConnected() outside of the synchronized block or we could dead
lock. |
| 204 final boolean isConnected = isConnected(); |
| 205 synchronized (mBindingLock) { |
| 206 if (!isConnected) { |
| 207 Log.w(TAG, "The connection is not bound for %d", getPid()); |
| 208 return; |
| 209 } |
| 210 mModerateBinding.bind(); |
| 211 } |
| 212 } |
| 213 |
| 214 public void removeModerateBinding() { |
| 215 // Call isConnected() outside of the synchronized block or we could dead
lock. |
| 216 final boolean isConnected = isConnected(); |
| 217 synchronized (mBindingLock) { |
| 218 if (!isConnected) { |
| 219 Log.w(TAG, "The connection is not bound for %d", getPid()); |
| 220 return; |
| 221 } |
| 222 mModerateBinding.unbind(); |
| 223 } |
| 224 } |
| 225 } |
OLD | NEW |