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