| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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.content.browser; |
| 6 | 6 |
| 7 import android.content.ComponentName; | 7 import android.content.ComponentName; |
| 8 import android.content.Context; | 8 import android.content.Context; |
| 9 import android.content.Intent; | 9 import android.content.Intent; |
| 10 import android.content.ServiceConnection; | 10 import android.content.ServiceConnection; |
| 11 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
| 12 import android.content.pm.ServiceInfo; | 12 import android.content.pm.ServiceInfo; |
| 13 import android.os.Build; | 13 import android.os.Build; |
| 14 import android.os.Bundle; | 14 import android.os.Bundle; |
| 15 import android.os.IBinder; | 15 import android.os.IBinder; |
| 16 import android.os.RemoteException; | 16 import android.os.RemoteException; |
| 17 | 17 |
| 18 import org.chromium.base.Log; | 18 import org.chromium.base.Log; |
| 19 import org.chromium.base.TraceEvent; | 19 import org.chromium.base.TraceEvent; |
| 20 import org.chromium.base.VisibleForTesting; | 20 import org.chromium.base.VisibleForTesting; |
| 21 import org.chromium.base.process_launcher.ChildProcessCreationParams; | 21 import org.chromium.base.process_launcher.ChildProcessCreationParams; |
| 22 import org.chromium.base.process_launcher.FileDescriptorInfo; | 22 import org.chromium.base.process_launcher.FileDescriptorInfo; |
| 23 import org.chromium.base.process_launcher.ICallbackInt; | 23 import org.chromium.base.process_launcher.ICallbackInt; |
| 24 import org.chromium.base.process_launcher.IChildProcessService; | 24 import org.chromium.base.process_launcher.IChildProcessService; |
| 25 | 25 |
| 26 import java.io.IOException; | 26 import java.io.IOException; |
| 27 | 27 |
| 28 import javax.annotation.Nullable; | 28 import javax.annotation.Nullable; |
| 29 import javax.annotation.concurrent.GuardedBy; |
| 29 | 30 |
| 30 /** | 31 /** |
| 31 * Manages a connection between the browser activity and a child service. | 32 * Manages a connection between the browser activity and a child service. |
| 32 */ | 33 */ |
| 33 public class ChildProcessConnectionImpl implements ChildProcessConnection { | 34 public abstract class BaseChildProcessConnection { |
| 34 private final Context mContext; | 35 private static final String TAG = "BaseChildProcessConn"; |
| 35 private final int mServiceNumber; | |
| 36 private final boolean mInSandbox; | |
| 37 private final ChildProcessConnection.DeathCallback mDeathCallback; | |
| 38 private final ComponentName mServiceName; | |
| 39 | 36 |
| 40 // Synchronization: While most internal flow occurs on the UI thread, the pu
blic API | 37 /** |
| 41 // (specifically start and stop) may be called from any thread, hence all en
try point methods | 38 * Used to notify the consumer about disconnection of the service. This call
back is provided |
| 42 // into the class are synchronized on the lock to protect access to these me
mbers. | 39 * earlier than ConnectionCallbacks below, as a child process might die befo
re the connection is |
| 43 private final Object mLock = new Object(); | 40 * fully set up. |
| 44 private IChildProcessService mService; | 41 */ |
| 45 // Set to true when the service connection callback runs. This differs from | 42 interface DeathCallback { |
| 46 // mServiceConnectComplete, which tracks that the connection completed succe
ssfully. | 43 // Called on Launcher thread. |
| 47 private boolean mDidOnServiceConnected; | 44 void onChildProcessDied(BaseChildProcessConnection connection); |
| 48 // Set to true when the service connected successfully. | |
| 49 private boolean mServiceConnectComplete; | |
| 50 // Set to true when the service disconnects, as opposed to being properly cl
osed. This happens | |
| 51 // when the process crashes or gets killed by the system out-of-memory kille
r. | |
| 52 private boolean mServiceDisconnected; | |
| 53 // When the service disconnects (i.e. mServiceDisconnected is set to true),
the status of the | |
| 54 // oom bindings is stashed here for future inspection. | |
| 55 private boolean mWasOomProtected; | |
| 56 private int mPid; // Process ID of the corresponding child process. | |
| 57 // Initial binding protects the newly spawned process from being killed befo
re it is put to use, | |
| 58 // it is maintained between calls to start() and removeInitialBinding(). | |
| 59 private ChildServiceConnection mInitialBinding; | |
| 60 // Strong binding will make the service priority equal to the priority of th
e activity. We want | |
| 61 // the OS to be able to kill background renderers as it kills other backgrou
nd apps, so strong | |
| 62 // bindings are maintained only for services that are active at the moment (
between | |
| 63 // addStrongBinding() and removeStrongBinding()). | |
| 64 private ChildServiceConnection mStrongBinding; | |
| 65 // Low priority binding maintained in the entire lifetime of the connection,
i.e. between calls | |
| 66 // to start() and stop(). | |
| 67 private ChildServiceConnection mWaivedBinding; | |
| 68 // Incremented on addStrongBinding(), decremented on removeStrongBinding(). | |
| 69 private int mStrongBindingCount; | |
| 70 // Moderate binding will make the service priority equal to the priority of
a visible process | |
| 71 // while the app is in the foreground. It will stay bound only while the app
is in the | |
| 72 // foreground to protect a background process from the system out-of-memory
killer. | |
| 73 private ChildServiceConnection mModerateBinding; | |
| 74 | |
| 75 // Parameters passed to the child process through the service binding intent
. | |
| 76 // If the service gets recreated by the framework the intent will be reused,
so these parameters | |
| 77 // should be common to all processes of that type. | |
| 78 private final Bundle mChildProcessCommonParameters; | |
| 79 | |
| 80 private final boolean mAlwaysInForeground; | |
| 81 private final ChildProcessCreationParams mCreationParams; | |
| 82 | |
| 83 // Caches whether non-sandboxed and sandboxed services require an extra | |
| 84 // binding flag provided via ChildProcessCreationParams. | |
| 85 // TODO(mnaganov): Get rid of it after the release of the next Android SDK. | |
| 86 private static Boolean sNeedsExtrabindFlags[] = new Boolean[2]; | |
| 87 | |
| 88 private static final String TAG = "ChildProcessConnect"; | |
| 89 | |
| 90 private static class ConnectionParams { | |
| 91 final String[] mCommandLine; | |
| 92 final FileDescriptorInfo[] mFilesToBeMapped; | |
| 93 final IBinder mCallback; | |
| 94 | |
| 95 ConnectionParams( | |
| 96 String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBin
der callback) { | |
| 97 mCommandLine = commandLine; | |
| 98 mFilesToBeMapped = filesToBeMapped; | |
| 99 mCallback = callback; | |
| 100 } | |
| 101 } | 45 } |
| 102 | 46 |
| 103 // This is set in start() and is used in onServiceConnected(). | 47 /** |
| 104 private ChildProcessConnection.StartCallback mStartCallback; | 48 * Used to notify the consumer about the process start. These callbacks will
be invoked before |
| 49 * the ConnectionCallbacks. |
| 50 */ |
| 51 interface StartCallback { |
| 52 /** |
| 53 * Called when the child process has successfully started and is ready f
or connection |
| 54 * setup. |
| 55 */ |
| 56 void onChildStarted(); |
| 105 | 57 |
| 106 // This is set in setupConnection() and is later used in doConnectionSetupLo
cked(), after which | 58 /** |
| 107 // the variable is cleared. Therefore this is only valid while the connectio
n is being set up. | 59 * Called when the child process failed to start. This can happen if the
process is already |
| 108 private ConnectionParams mConnectionParams; | 60 * in use by another client. |
| 61 */ |
| 62 void onChildStartFailed(); |
| 63 } |
| 109 | 64 |
| 110 // Callback provided in setupConnection() that will communicate the result t
o the caller. This | 65 /** |
| 111 // has to be called exactly once after setupConnection(), even if setup fail
s, so that the | 66 * Used to notify the consumer about the connection being established. |
| 112 // caller can free up resources associated with the setup attempt. This is s
et to null after the | 67 */ |
| 113 // call. | 68 interface ConnectionCallback { |
| 114 private ChildProcessConnection.ConnectionCallback mConnectionCallback; | 69 /** |
| 70 * Called when the connection to the service is established. |
| 71 * @param connecion the connection object to the child process |
| 72 */ |
| 73 void onConnected(BaseChildProcessConnection connection); |
| 74 } |
| 115 | 75 |
| 116 private class ChildServiceConnection implements ServiceConnection { | 76 /** Used to create specialization connection instances. */ |
| 77 interface Factory { |
| 78 BaseChildProcessConnection create(Context context, int number, boolean s
andboxed, |
| 79 DeathCallback deathCallback, String serviceClassName, |
| 80 Bundle childProcessCommonParameters, ChildProcessCreationParams
creationParams); |
| 81 } |
| 82 |
| 83 /** Interface representing a connection to the Android service. Can be mocke
d in unit-tests. */ |
| 84 protected interface ChildServiceConnection { |
| 85 boolean bind(); |
| 86 void unbind(); |
| 87 boolean isBound(); |
| 88 } |
| 89 |
| 90 /** Implementation of ChildServiceConnection that does connect to a service.
*/ |
| 91 protected class ChildServiceConnectionImpl |
| 92 implements ChildServiceConnection, ServiceConnection { |
| 93 private final int mBindFlags; |
| 117 private boolean mBound; | 94 private boolean mBound; |
| 118 | 95 |
| 119 private final int mBindFlags; | |
| 120 | |
| 121 private Intent createServiceBindIntent() { | 96 private Intent createServiceBindIntent() { |
| 122 Intent intent = new Intent(); | 97 Intent intent = new Intent(); |
| 123 if (mCreationParams != null) { | 98 if (mCreationParams != null) { |
| 124 mCreationParams.addIntentExtras(intent); | 99 mCreationParams.addIntentExtras(intent); |
| 125 } | 100 } |
| 126 intent.setComponent(mServiceName); | 101 intent.setComponent(mServiceName); |
| 127 return intent; | 102 return intent; |
| 128 } | 103 } |
| 129 | 104 |
| 130 public ChildServiceConnection(int bindFlags) { | 105 private ChildServiceConnectionImpl(int bindFlags) { |
| 131 mBindFlags = bindFlags; | 106 mBindFlags = bindFlags; |
| 132 } | 107 } |
| 133 | 108 |
| 134 boolean bind() { | 109 @Override |
| 110 public boolean bind() { |
| 135 if (!mBound) { | 111 if (!mBound) { |
| 136 try { | 112 try { |
| 137 TraceEvent.begin("ChildProcessConnectionImpl.ChildServiceCon
nection.bind"); | 113 TraceEvent.begin("BaseChildProcessConnection.ChildServiceCon
nection.bind"); |
| 138 Intent intent = createServiceBindIntent(); | 114 Intent intent = createServiceBindIntent(); |
| 139 if (mChildProcessCommonParameters != null) { | 115 if (mChildProcessCommonParameters != null) { |
| 140 intent.putExtras(mChildProcessCommonParameters); | 116 intent.putExtras(mChildProcessCommonParameters); |
| 141 } | 117 } |
| 142 mBound = mContext.bindService(intent, this, mBindFlags); | 118 mBound = mContext.bindService(intent, this, mBindFlags); |
| 143 } finally { | 119 } finally { |
| 144 TraceEvent.end("ChildProcessConnectionImpl.ChildServiceConne
ction.bind"); | 120 TraceEvent.end("BaseChildProcessConnection.ChildServiceConne
ction.bind"); |
| 145 } | 121 } |
| 146 } | 122 } |
| 147 return mBound; | 123 return mBound; |
| 148 } | 124 } |
| 149 | 125 |
| 150 void unbind() { | 126 @Override |
| 127 public void unbind() { |
| 151 if (mBound) { | 128 if (mBound) { |
| 152 mContext.unbindService(this); | 129 mContext.unbindService(this); |
| 153 mBound = false; | 130 mBound = false; |
| 154 } | 131 } |
| 155 } | 132 } |
| 156 | 133 |
| 157 boolean isBound() { | 134 @Override |
| 135 public boolean isBound() { |
| 158 return mBound; | 136 return mBound; |
| 159 } | 137 } |
| 160 | 138 |
| 161 @Override | 139 @Override |
| 162 public void onServiceConnected(ComponentName className, final IBinder se
rvice) { | 140 public void onServiceConnected(ComponentName className, final IBinder se
rvice) { |
| 163 LauncherThread.post(new Runnable() { | 141 LauncherThread.post(new Runnable() { |
| 164 @Override | 142 @Override |
| 165 public void run() { | 143 public void run() { |
| 166 ChildProcessConnectionImpl.this.onServiceConnectedOnLauncher
Thread(service); | 144 BaseChildProcessConnection.this.onServiceConnectedOnLauncher
Thread(service); |
| 167 } | 145 } |
| 168 }); | 146 }); |
| 169 } | 147 } |
| 170 | 148 |
| 171 // Called on the main thread to notify that the child service did not di
sconnect gracefully. | 149 // Called on the main thread to notify that the child service did not di
sconnect gracefully. |
| 172 @Override | 150 @Override |
| 173 public void onServiceDisconnected(ComponentName className) { | 151 public void onServiceDisconnected(ComponentName className) { |
| 174 LauncherThread.post(new Runnable() { | 152 LauncherThread.post(new Runnable() { |
| 175 @Override | 153 @Override |
| 176 public void run() { | 154 public void run() { |
| 177 ChildProcessConnectionImpl.this.onServiceDisconnectedOnLaunc
herThread(); | 155 BaseChildProcessConnection.this.onServiceDisconnectedOnLaunc
herThread(); |
| 178 } | 156 } |
| 179 }); | 157 }); |
| 180 } | 158 } |
| 181 } | 159 } |
| 182 | 160 |
| 183 ChildProcessConnectionImpl(Context context, int number, boolean inSandbox, | 161 // Caches whether non-sandboxed and sandboxed services require an extra |
| 184 ChildProcessConnection.DeathCallback deathCallback, String serviceCl
assName, | 162 // binding flag provided via ChildProcessCreationParams. |
| 185 Bundle childProcessCommonParameters, boolean alwaysInForeground, | 163 // TODO(mnaganov): Get rid of it after the release of the next Android SDK. |
| 186 ChildProcessCreationParams creationParams) { | 164 private static Boolean sNeedsExtrabindFlags[] = new Boolean[2]; |
| 165 private final Context mContext; |
| 166 private final int mServiceNumber; |
| 167 private final boolean mSandboxed; |
| 168 private final BaseChildProcessConnection.DeathCallback mDeathCallback; |
| 169 private final ComponentName mServiceName; |
| 170 |
| 171 // Parameters passed to the child process through the service binding intent
. |
| 172 // If the service gets recreated by the framework the intent will be reused,
so these parameters |
| 173 // should be common to all processes of that type. |
| 174 private final Bundle mChildProcessCommonParameters; |
| 175 |
| 176 private final ChildProcessCreationParams mCreationParams; |
| 177 |
| 178 private static class ConnectionParams { |
| 179 final String[] mCommandLine; |
| 180 final FileDescriptorInfo[] mFilesToBeMapped; |
| 181 final IBinder mCallback; |
| 182 |
| 183 ConnectionParams( |
| 184 String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBin
der callback) { |
| 185 mCommandLine = commandLine; |
| 186 mFilesToBeMapped = filesToBeMapped; |
| 187 mCallback = callback; |
| 188 } |
| 189 } |
| 190 |
| 191 // Synchronization: While most internal flow occurs on the UI thread, the pu
blic API |
| 192 // (specifically start and stop) may be called from any thread, hence all en
try point methods |
| 193 // into the class are synchronized on the lock to protect access to these me
mbers. |
| 194 // TODO(jcivelli): crbug.com/714657 remove this lock. |
| 195 private final Object mLock = new Object(); |
| 196 |
| 197 // This is set in start() and is used in onServiceConnected(). |
| 198 @GuardedBy("mLock") |
| 199 private StartCallback mStartCallback; |
| 200 |
| 201 // This is set in setupConnection() and is later used in doConnectionSetupLo
cked(), after which |
| 202 // the variable is cleared. Therefore this is only valid while the connectio
n is being set up. |
| 203 @GuardedBy("mLock") |
| 204 private ConnectionParams mConnectionParams; |
| 205 |
| 206 // Callback provided in setupConnection() that will communicate the result t
o the caller. This |
| 207 // has to be called exactly once after setupConnection(), even if setup fail
s, so that the |
| 208 // caller can free up resources associated with the setup attempt. This is s
et to null after the |
| 209 // call. |
| 210 @GuardedBy("mLock") |
| 211 private ConnectionCallback mConnectionCallback; |
| 212 |
| 213 @GuardedBy("mLock") |
| 214 private IChildProcessService mService; |
| 215 |
| 216 // Set to true when the service connection callback runs. This differs from |
| 217 // mServiceConnectComplete, which tracks that the connection completed succe
ssfully. |
| 218 @GuardedBy("mLock") |
| 219 private boolean mDidOnServiceConnected; |
| 220 |
| 221 // Set to true when the service connected successfully. |
| 222 @GuardedBy("mLock") |
| 223 private boolean mServiceConnectComplete; |
| 224 |
| 225 // Set to true when the service disconnects, as opposed to being properly cl
osed. This happens |
| 226 // when the process crashes or gets killed by the system out-of-memory kille
r. |
| 227 @GuardedBy("mLock") |
| 228 private boolean mServiceDisconnected; |
| 229 |
| 230 // Process ID of the corresponding child process. |
| 231 @GuardedBy("mLock") |
| 232 private int mPid; |
| 233 |
| 234 protected BaseChildProcessConnection(Context context, int number, boolean sa
ndboxed, |
| 235 DeathCallback deathCallback, String serviceClassName, |
| 236 Bundle childProcessCommonParameters, ChildProcessCreationParams crea
tionParams) { |
| 187 mContext = context; | 237 mContext = context; |
| 188 mServiceNumber = number; | 238 mServiceNumber = number; |
| 189 mInSandbox = inSandbox; | 239 mSandboxed = sandboxed; |
| 190 mDeathCallback = deathCallback; | 240 mDeathCallback = deathCallback; |
| 191 String packageName = | 241 String packageName = |
| 192 creationParams != null ? creationParams.getPackageName() : conte
xt.getPackageName(); | 242 creationParams != null ? creationParams.getPackageName() : conte
xt.getPackageName(); |
| 193 mServiceName = new ComponentName(packageName, serviceClassName + mServic
eNumber); | 243 mServiceName = new ComponentName(packageName, serviceClassName + mServic
eNumber); |
| 194 mChildProcessCommonParameters = childProcessCommonParameters; | 244 mChildProcessCommonParameters = childProcessCommonParameters; |
| 195 mAlwaysInForeground = alwaysInForeground; | |
| 196 mCreationParams = creationParams; | 245 mCreationParams = creationParams; |
| 197 int initialFlags = Context.BIND_AUTO_CREATE; | |
| 198 if (mAlwaysInForeground) initialFlags |= Context.BIND_IMPORTANT; | |
| 199 int extraBindFlags = 0; | |
| 200 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mCreationParams !=
null | |
| 201 && mCreationParams.getIsExternalService() | |
| 202 && isExportedService(inSandbox, mContext, mServiceName)) { | |
| 203 extraBindFlags = Context.BIND_EXTERNAL_SERVICE; | |
| 204 } | |
| 205 mInitialBinding = new ChildServiceConnection(initialFlags | extraBindFla
gs); | |
| 206 mStrongBinding = new ChildServiceConnection( | |
| 207 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFla
gs); | |
| 208 mWaivedBinding = new ChildServiceConnection( | |
| 209 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBi
ndFlags); | |
| 210 mModerateBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE |
extraBindFlags); | |
| 211 } | 246 } |
| 212 | 247 |
| 213 private static boolean isExportedService(boolean inSandbox, Context context, | 248 public final Context getContext() { |
| 214 ComponentName serviceName) { | 249 return mContext; |
| 215 // Check for the cached value first. It is assumed that all pooled child
services | |
| 216 // have identical attributes in the manifest. | |
| 217 final int arrayIndex = inSandbox ? 1 : 0; | |
| 218 if (sNeedsExtrabindFlags[arrayIndex] != null) { | |
| 219 return sNeedsExtrabindFlags[arrayIndex].booleanValue(); | |
| 220 } | |
| 221 boolean result = false; | |
| 222 try { | |
| 223 PackageManager packageManager = context.getPackageManager(); | |
| 224 ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName,
0); | |
| 225 result = serviceInfo.exported; | |
| 226 } catch (PackageManager.NameNotFoundException e) { | |
| 227 Log.e(TAG, "Could not retrieve info about service %s", serviceName,
e); | |
| 228 } | |
| 229 sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result); | |
| 230 return result; | |
| 231 } | 250 } |
| 232 | 251 |
| 233 @Override | 252 public final int getServiceNumber() { |
| 234 public int getServiceNumber() { | |
| 235 return mServiceNumber; | 253 return mServiceNumber; |
| 236 } | 254 } |
| 237 | 255 |
| 238 @Override | 256 public final boolean isSandboxed() { |
| 239 public boolean isInSandbox() { | 257 return mSandboxed; |
| 240 return mInSandbox; | |
| 241 } | 258 } |
| 242 | 259 |
| 243 @Override | 260 public final String getPackageName() { |
| 244 public String getPackageName() { | |
| 245 return mCreationParams != null ? mCreationParams.getPackageName() | 261 return mCreationParams != null ? mCreationParams.getPackageName() |
| 246 : mContext.getPackageName(); | 262 : mContext.getPackageName(); |
| 247 } | 263 } |
| 248 | 264 |
| 249 @Override | 265 public final ChildProcessCreationParams getCreationParams() { |
| 250 public ChildProcessCreationParams getCreationParams() { | |
| 251 return mCreationParams; | 266 return mCreationParams; |
| 252 } | 267 } |
| 253 | 268 |
| 254 @Override | 269 public final IChildProcessService getService() { |
| 255 public IChildProcessService getService() { | |
| 256 synchronized (mLock) { | 270 synchronized (mLock) { |
| 257 return mService; | 271 return mService; |
| 258 } | 272 } |
| 259 } | 273 } |
| 260 | 274 |
| 261 @Override | 275 public final ComponentName getServiceName() { |
| 276 return mServiceName; |
| 277 } |
| 278 |
| 279 /** |
| 280 * @return the connection pid, or 0 if not yet connected |
| 281 */ |
| 262 public int getPid() { | 282 public int getPid() { |
| 263 synchronized (mLock) { | 283 synchronized (mLock) { |
| 264 return mPid; | 284 return mPid; |
| 265 } | 285 } |
| 266 } | 286 } |
| 267 | 287 |
| 268 @Override | 288 /** |
| 269 public void start(ChildProcessConnection.StartCallback startCallback) { | 289 * Starts a connection to an IChildProcessService. This must be followed by
a call to |
| 290 * setupConnection() to setup the connection parameters. start() and setupCo
nnection() are |
| 291 * separate to allow to pass whatever parameters are available in start(), a
nd complete the |
| 292 * remainder later while reducing the connection setup latency. |
| 293 * @param startCallback (optional) callback when the child process starts or
fails to start. |
| 294 */ |
| 295 public void start(StartCallback startCallback) { |
| 270 try { | 296 try { |
| 271 TraceEvent.begin("ChildProcessConnectionImpl.start"); | 297 TraceEvent.begin("BaseChildProcessConnection.start"); |
| 272 assert LauncherThread.runningOnLauncherThread(); | 298 assert LauncherThread.runningOnLauncherThread(); |
| 273 synchronized (mLock) { | 299 synchronized (mLock) { |
| 274 assert mConnectionParams == null : | 300 assert mConnectionParams |
| 275 "setupConnection() called before start() in ChildProcess
ConnectionImpl."; | 301 == null |
| 302 : "setupConnection() called before start() in BaseChildP
rocessConnection."; |
| 276 | 303 |
| 277 mStartCallback = startCallback; | 304 mStartCallback = startCallback; |
| 278 | 305 |
| 279 if (!mInitialBinding.bind()) { | 306 if (!bind()) { |
| 280 Log.e(TAG, "Failed to establish the service connection."); | 307 Log.e(TAG, "Failed to establish the service connection."); |
| 281 // We have to notify the caller so that they can free-up ass
ociated resources. | 308 // We have to notify the caller so that they can free-up ass
ociated resources. |
| 282 // TODO(ppi): Can we hard-fail here? | 309 // TODO(ppi): Can we hard-fail here? |
| 283 mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl
.this); | 310 mDeathCallback.onChildProcessDied(BaseChildProcessConnection
.this); |
| 284 } else { | |
| 285 mWaivedBinding.bind(); | |
| 286 } | 311 } |
| 287 } | 312 } |
| 288 } finally { | 313 } finally { |
| 289 TraceEvent.end("ChildProcessConnectionImpl.start"); | 314 TraceEvent.end("BaseChildProcessConnection.start"); |
| 290 } | 315 } |
| 291 } | 316 } |
| 292 | 317 |
| 293 @Override | 318 /** |
| 319 * Setups the connection after it was started with start(). |
| 320 * @param commandLine (optional) will be ignored if the command line was alr
eady sent in start() |
| 321 * @param filesToBeMapped a list of file descriptors that should be register
ed |
| 322 * @param callback optional client specified callbacks that the child can us
e to communicate |
| 323 * with the parent process |
| 324 * @param connectionCallback will be called exactly once after the connectio
n is set up or the |
| 325 * setup fails |
| 326 */ |
| 294 public void setupConnection(String[] commandLine, FileDescriptorInfo[] files
ToBeMapped, | 327 public void setupConnection(String[] commandLine, FileDescriptorInfo[] files
ToBeMapped, |
| 295 @Nullable IBinder callback, ConnectionCallback connectionCallback) { | 328 @Nullable IBinder callback, ConnectionCallback connectionCallback) { |
| 296 assert LauncherThread.runningOnLauncherThread(); | 329 assert LauncherThread.runningOnLauncherThread(); |
| 297 synchronized (mLock) { | 330 synchronized (mLock) { |
| 298 assert mConnectionParams == null; | 331 assert mConnectionParams == null; |
| 299 if (mServiceDisconnected) { | 332 if (mServiceDisconnected) { |
| 300 Log.w(TAG, "Tried to setup a connection that already disconnecte
d."); | 333 Log.w(TAG, "Tried to setup a connection that already disconnecte
d."); |
| 301 connectionCallback.onConnected(0); | 334 connectionCallback.onConnected(null); |
| 302 return; | 335 return; |
| 303 } | 336 } |
| 304 try { | 337 try { |
| 305 TraceEvent.begin("ChildProcessConnectionImpl.setupConnection"); | 338 TraceEvent.begin("BaseChildProcessConnection.setupConnection"); |
| 306 mConnectionCallback = connectionCallback; | 339 mConnectionCallback = connectionCallback; |
| 307 mConnectionParams = new ConnectionParams(commandLine, filesToBeM
apped, callback); | 340 mConnectionParams = new ConnectionParams(commandLine, filesToBeM
apped, callback); |
| 308 // Run the setup if the service is already connected. If not, | 341 // Run the setup if the service is already connected. If not, |
| 309 // doConnectionSetupLocked() will be called from onServiceConnec
ted(). | 342 // doConnectionSetupLocked() will be called from onServiceConnec
ted(). |
| 310 if (mServiceConnectComplete) { | 343 if (mServiceConnectComplete) { |
| 311 doConnectionSetupLocked(); | 344 doConnectionSetupLocked(); |
| 312 } | 345 } |
| 313 } finally { | 346 } finally { |
| 314 TraceEvent.end("ChildProcessConnectionImpl.setupConnection"); | 347 TraceEvent.end("BaseChildProcessConnection.setupConnection"); |
| 315 } | 348 } |
| 316 } | 349 } |
| 317 } | 350 } |
| 318 | 351 |
| 319 @Override | 352 /** |
| 353 * Terminates the connection to IChildProcessService, closing all bindings.
It is safe to call |
| 354 * this multiple times. |
| 355 */ |
| 320 public void stop() { | 356 public void stop() { |
| 321 synchronized (mLock) { | 357 synchronized (mLock) { |
| 322 mInitialBinding.unbind(); | 358 unbind(); |
| 323 mStrongBinding.unbind(); | 359 mService = null; |
| 324 mWaivedBinding.unbind(); | |
| 325 mModerateBinding.unbind(); | |
| 326 mStrongBindingCount = 0; | |
| 327 if (mService != null) { | |
| 328 mService = null; | |
| 329 } | |
| 330 mConnectionParams = null; | 360 mConnectionParams = null; |
| 331 } | 361 } |
| 332 } | 362 } |
| 333 | 363 |
| 334 private void onServiceConnectedOnLauncherThread(IBinder service) { | 364 private void onServiceConnectedOnLauncherThread(IBinder service) { |
| 335 assert LauncherThread.runningOnLauncherThread(); | 365 assert LauncherThread.runningOnLauncherThread(); |
| 336 synchronized (mLock) { | 366 synchronized (mLock) { |
| 337 // A flag from the parent class ensures we run the post-connection l
ogic only once | 367 // A flag from the parent class ensures we run the post-connection l
ogic only once |
| 338 // (instead of once per each ChildServiceConnection). | 368 // (instead of once per each ChildServiceConnection). |
| 339 if (mDidOnServiceConnected) { | 369 if (mDidOnServiceConnected) { |
| 340 return; | 370 return; |
| 341 } | 371 } |
| 342 try { | 372 try { |
| 343 TraceEvent.begin( | 373 TraceEvent.begin( |
| 344 "ChildProcessConnectionImpl.ChildServiceConnection.onSer
viceConnected"); | 374 "BaseChildProcessConnection.ChildServiceConnection.onSer
viceConnected"); |
| 345 mDidOnServiceConnected = true; | 375 mDidOnServiceConnected = true; |
| 346 mService = IChildProcessService.Stub.asInterface(service); | 376 mService = IChildProcessService.Stub.asInterface(service); |
| 347 | 377 |
| 348 StartCallback startCallback = mStartCallback; | 378 StartCallback startCallback = mStartCallback; |
| 349 mStartCallback = null; | 379 mStartCallback = null; |
| 350 | 380 |
| 351 final boolean bindCheck = | 381 final boolean bindCheck = |
| 352 mCreationParams != null && mCreationParams.getBindToCall
erCheck(); | 382 mCreationParams != null && mCreationParams.getBindToCall
erCheck(); |
| 353 boolean boundToUs = false; | 383 boolean boundToUs = false; |
| 354 try { | 384 try { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 374 | 404 |
| 375 mServiceConnectComplete = true; | 405 mServiceConnectComplete = true; |
| 376 | 406 |
| 377 // Run the setup if the connection parameters have already been
provided. If | 407 // Run the setup if the connection parameters have already been
provided. If |
| 378 // not, doConnectionSetupLocked() will be called from setupConne
ction(). | 408 // not, doConnectionSetupLocked() will be called from setupConne
ction(). |
| 379 if (mConnectionParams != null) { | 409 if (mConnectionParams != null) { |
| 380 doConnectionSetupLocked(); | 410 doConnectionSetupLocked(); |
| 381 } | 411 } |
| 382 } finally { | 412 } finally { |
| 383 TraceEvent.end( | 413 TraceEvent.end( |
| 384 "ChildProcessConnectionImpl.ChildServiceConnection.onSer
viceConnected"); | 414 "BaseChildProcessConnection.ChildServiceConnection.onSer
viceConnected"); |
| 385 } | 415 } |
| 386 } | 416 } |
| 387 } | 417 } |
| 388 | 418 |
| 389 private void onServiceDisconnectedOnLauncherThread() { | 419 private void onServiceDisconnectedOnLauncherThread() { |
| 390 assert LauncherThread.runningOnLauncherThread(); | 420 assert LauncherThread.runningOnLauncherThread(); |
| 391 synchronized (mLock) { | 421 synchronized (mLock) { |
| 392 // Ensure that the disconnection logic runs only once (instead of on
ce per each | 422 // Ensure that the disconnection logic runs only once (instead of on
ce per each |
| 393 // ChildServiceConnection). | 423 // ChildServiceConnection). |
| 394 if (mServiceDisconnected) { | 424 if (mServiceDisconnected) { |
| 395 return; | 425 return; |
| 396 } | 426 } |
| 397 // Stash the status of the oom bindings, since stop() will release a
ll bindings. | |
| 398 mWasOomProtected = isCurrentlyOomProtected(); | |
| 399 mServiceDisconnected = true; | 427 mServiceDisconnected = true; |
| 400 Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d",
mPid); | 428 Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d",
mPid); |
| 401 stop(); // We don't want to auto-restart on crash. Let the browser d
o that. | 429 stop(); // We don't want to auto-restart on crash. Let the browser d
o that. |
| 402 mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this); | 430 mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this); |
| 403 // If we have a pending connection callback, we need to communicate
the failure to | 431 // If we have a pending connection callback, we need to communicate
the failure to |
| 404 // the caller. | 432 // the caller. |
| 405 if (mConnectionCallback != null) { | 433 if (mConnectionCallback != null) { |
| 406 mConnectionCallback.onConnected(0); | 434 mConnectionCallback.onConnected(null); |
| 407 } | 435 } |
| 408 mConnectionCallback = null; | 436 mConnectionCallback = null; |
| 409 } | 437 } |
| 410 } | 438 } |
| 411 | 439 |
| 412 private void onSetupConnectionResult(int pid) { | 440 private void onSetupConnectionResult(int pid) { |
| 413 synchronized (mLock) { | 441 synchronized (mLock) { |
| 414 mPid = pid; | 442 mPid = pid; |
| 415 assert mPid != 0 : "Child service claims to be run by a process of p
id=0."; | 443 assert mPid != 0 : "Child service claims to be run by a process of p
id=0."; |
| 416 | 444 |
| 417 if (mConnectionCallback != null) { | 445 if (mConnectionCallback != null) { |
| 418 mConnectionCallback.onConnected(mPid); | 446 mConnectionCallback.onConnected(this); |
| 419 } | 447 } |
| 420 mConnectionCallback = null; | 448 mConnectionCallback = null; |
| 421 } | 449 } |
| 422 } | 450 } |
| 423 | 451 |
| 424 /** | 452 /** |
| 425 * Called after the connection parameters have been set (in setupConnection(
)) *and* a | 453 * Called after the connection parameters have been set (in setupConnection(
)) *and* a |
| 426 * connection has been established (as signaled by onServiceConnected()). Th
ese two events can | 454 * connection has been established (as signaled by onServiceConnected()). Th
ese two events can |
| 427 * happen in any order. Has to be called with mLock. | 455 * happen in any order. Has to be called with mLock. |
| 428 */ | 456 */ |
| 457 @GuardedBy("mLock") |
| 429 private void doConnectionSetupLocked() { | 458 private void doConnectionSetupLocked() { |
| 430 try { | 459 try { |
| 431 TraceEvent.begin("ChildProcessConnectionImpl.doConnectionSetupLocked
"); | 460 TraceEvent.begin("BaseChildProcessConnection.doConnectionSetupLocked
"); |
| 432 assert mServiceConnectComplete && mService != null; | 461 assert mServiceConnectComplete && mService != null; |
| 433 assert mConnectionParams != null; | 462 assert mConnectionParams != null; |
| 434 | 463 |
| 435 Bundle bundle = ChildProcessLauncher.createsServiceBundle( | 464 Bundle bundle = ChildProcessLauncher.createsServiceBundle( |
| 436 mConnectionParams.mCommandLine, mConnectionParams.mFilesToBe
Mapped); | 465 mConnectionParams.mCommandLine, mConnectionParams.mFilesToBe
Mapped); |
| 437 ICallbackInt pidCallback = new ICallbackInt.Stub() { | 466 ICallbackInt pidCallback = new ICallbackInt.Stub() { |
| 438 @Override | 467 @Override |
| 439 public void call(final int pid) { | 468 public void call(final int pid) { |
| 440 LauncherThread.post(new Runnable() { | 469 LauncherThread.post(new Runnable() { |
| 441 @Override | 470 @Override |
| (...skipping 11 matching lines...) Expand all Loading... |
| 453 // We proactively close the FDs rather than wait for GC & finalizer. | 482 // We proactively close the FDs rather than wait for GC & finalizer. |
| 454 try { | 483 try { |
| 455 for (FileDescriptorInfo fileInfo : mConnectionParams.mFilesToBeM
apped) { | 484 for (FileDescriptorInfo fileInfo : mConnectionParams.mFilesToBeM
apped) { |
| 456 fileInfo.fd.close(); | 485 fileInfo.fd.close(); |
| 457 } | 486 } |
| 458 } catch (IOException ioe) { | 487 } catch (IOException ioe) { |
| 459 Log.w(TAG, "Failed to close FD.", ioe); | 488 Log.w(TAG, "Failed to close FD.", ioe); |
| 460 } | 489 } |
| 461 mConnectionParams = null; | 490 mConnectionParams = null; |
| 462 } finally { | 491 } finally { |
| 463 TraceEvent.end("ChildProcessConnectionImpl.doConnectionSetupLocked")
; | 492 TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked")
; |
| 464 } | 493 } |
| 465 } | 494 } |
| 466 | 495 |
| 467 @Override | 496 /** Subclasses should implement this method to bind/unbind to the actual ser
vice. */ |
| 468 public boolean isInitialBindingBound() { | 497 protected abstract boolean bind(); |
| 469 synchronized (mLock) { | 498 protected abstract void unbind(); |
| 470 return mInitialBinding.isBound(); | 499 |
| 471 } | 500 protected ChildServiceConnection createServiceConnection(int bindFlags) { |
| 501 return new ChildServiceConnectionImpl(bindFlags); |
| 472 } | 502 } |
| 473 | 503 |
| 474 @Override | 504 protected boolean shouldBindAsExportedService() { |
| 475 public boolean isStrongBindingBound() { | 505 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationPara
ms() != null |
| 476 synchronized (mLock) { | 506 && getCreationParams().getIsExternalService() |
| 477 return mStrongBinding.isBound(); | 507 && isExportedService(isSandboxed(), getContext(), getServiceName
()); |
| 478 } | |
| 479 } | 508 } |
| 480 | 509 |
| 481 @Override | 510 private static boolean isExportedService( |
| 482 public void removeInitialBinding() { | 511 boolean inSandbox, Context context, ComponentName serviceName) { |
| 483 synchronized (mLock) { | 512 // Check for the cached value first. It is assumed that all pooled child
services |
| 484 assert !mAlwaysInForeground; | 513 // have identical attributes in the manifest. |
| 485 mInitialBinding.unbind(); | 514 final int arrayIndex = inSandbox ? 1 : 0; |
| 515 if (sNeedsExtrabindFlags[arrayIndex] != null) { |
| 516 return sNeedsExtrabindFlags[arrayIndex].booleanValue(); |
| 486 } | 517 } |
| 518 boolean result = false; |
| 519 try { |
| 520 PackageManager packageManager = context.getPackageManager(); |
| 521 ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName,
0); |
| 522 result = serviceInfo.exported; |
| 523 } catch (PackageManager.NameNotFoundException e) { |
| 524 Log.e(TAG, "Could not retrieve info about service %s", serviceName,
e); |
| 525 } |
| 526 sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result); |
| 527 return result; |
| 487 } | 528 } |
| 488 | 529 |
| 489 @Override | 530 @VisibleForTesting |
| 490 public boolean isOomProtectedOrWasWhenDied() { | 531 public void crashServiceForTesting() throws RemoteException { |
| 491 synchronized (mLock) { | 532 synchronized (mLock) { |
| 492 if (mServiceDisconnected) { | 533 mService.crashIntentionallyForTesting(); |
| 493 return mWasOomProtected; | |
| 494 } else { | |
| 495 return isCurrentlyOomProtected(); | |
| 496 } | |
| 497 } | |
| 498 } | |
| 499 | |
| 500 private boolean isCurrentlyOomProtected() { | |
| 501 synchronized (mLock) { | |
| 502 assert !mServiceDisconnected; | |
| 503 if (mAlwaysInForeground) return ChildProcessLauncher.isApplicationIn
Foreground(); | |
| 504 return mInitialBinding.isBound() || mStrongBinding.isBound(); | |
| 505 } | |
| 506 } | |
| 507 | |
| 508 @Override | |
| 509 public void dropOomBindings() { | |
| 510 synchronized (mLock) { | |
| 511 assert !mAlwaysInForeground; | |
| 512 mInitialBinding.unbind(); | |
| 513 | |
| 514 mStrongBindingCount = 0; | |
| 515 mStrongBinding.unbind(); | |
| 516 | |
| 517 mModerateBinding.unbind(); | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 @Override | |
| 522 public void addStrongBinding() { | |
| 523 synchronized (mLock) { | |
| 524 if (mService == null) { | |
| 525 Log.w(TAG, "The connection is not bound for %d", mPid); | |
| 526 return; | |
| 527 } | |
| 528 if (mStrongBindingCount == 0) { | |
| 529 mStrongBinding.bind(); | |
| 530 } | |
| 531 mStrongBindingCount++; | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 @Override | |
| 536 public void removeStrongBinding() { | |
| 537 synchronized (mLock) { | |
| 538 if (mService == null) { | |
| 539 Log.w(TAG, "The connection is not bound for %d", mPid); | |
| 540 return; | |
| 541 } | |
| 542 assert mStrongBindingCount > 0; | |
| 543 mStrongBindingCount--; | |
| 544 if (mStrongBindingCount == 0) { | |
| 545 mStrongBinding.unbind(); | |
| 546 } | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 @Override | |
| 551 public boolean isModerateBindingBound() { | |
| 552 synchronized (mLock) { | |
| 553 return mModerateBinding.isBound(); | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 @Override | |
| 558 public void addModerateBinding() { | |
| 559 synchronized (mLock) { | |
| 560 if (mService == null) { | |
| 561 Log.w(TAG, "The connection is not bound for %d", mPid); | |
| 562 return; | |
| 563 } | |
| 564 mModerateBinding.bind(); | |
| 565 } | |
| 566 } | |
| 567 | |
| 568 @Override | |
| 569 public void removeModerateBinding() { | |
| 570 synchronized (mLock) { | |
| 571 if (mService == null) { | |
| 572 Log.w(TAG, "The connection is not bound for %d", mPid); | |
| 573 return; | |
| 574 } | |
| 575 mModerateBinding.unbind(); | |
| 576 } | 534 } |
| 577 } | 535 } |
| 578 | 536 |
| 579 @VisibleForTesting | 537 @VisibleForTesting |
| 580 public void crashServiceForTesting() throws RemoteException { | |
| 581 mService.crashIntentionallyForTesting(); | |
| 582 } | |
| 583 | |
| 584 @VisibleForTesting | |
| 585 public boolean isConnected() { | 538 public boolean isConnected() { |
| 586 return mService != null; | 539 synchronized (mLock) { |
| 540 return mService != null; |
| 541 } |
| 587 } | 542 } |
| 588 } | 543 } |
| OLD | NEW |