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 |