Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java

Issue 2828793002: Refactoring ChildProcessConnection. (Closed)
Patch Set: Refactoring ChildProcessConnection. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698