OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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; | |
8 import android.content.Context; | 7 import android.content.Context; |
9 import android.content.pm.ApplicationInfo; | |
10 import android.content.pm.PackageManager; | |
11 import android.os.Bundle; | 8 import android.os.Bundle; |
12 import android.os.IBinder; | 9 import android.os.IBinder; |
13 import android.os.RemoteException; | 10 import android.os.RemoteException; |
14 import android.text.TextUtils; | |
15 | 11 |
16 import org.chromium.base.CpuFeatures; | 12 import org.chromium.base.CpuFeatures; |
17 import org.chromium.base.Log; | 13 import org.chromium.base.Log; |
18 import org.chromium.base.ThreadUtils; | 14 import org.chromium.base.ThreadUtils; |
19 import org.chromium.base.TraceEvent; | 15 import org.chromium.base.TraceEvent; |
20 import org.chromium.base.VisibleForTesting; | 16 import org.chromium.base.VisibleForTesting; |
21 import org.chromium.base.library_loader.Linker; | 17 import org.chromium.base.library_loader.Linker; |
22 import org.chromium.base.process_launcher.ChildProcessCreationParams; | 18 import org.chromium.base.process_launcher.ChildProcessCreationParams; |
23 import org.chromium.base.process_launcher.FileDescriptorInfo; | 19 import org.chromium.base.process_launcher.FileDescriptorInfo; |
24 import org.chromium.content.app.ChromiumLinkerParams; | 20 import org.chromium.content.app.ChromiumLinkerParams; |
25 import org.chromium.content.app.PrivilegedProcessService; | |
26 import org.chromium.content.app.SandboxedProcessService; | |
27 import org.chromium.content.common.ContentSwitches; | 21 import org.chromium.content.common.ContentSwitches; |
28 | 22 |
29 import java.util.ArrayList; | |
30 import java.util.LinkedList; | |
31 import java.util.Map; | 23 import java.util.Map; |
32 import java.util.Queue; | |
33 import java.util.concurrent.ConcurrentHashMap; | 24 import java.util.concurrent.ConcurrentHashMap; |
34 | 25 |
35 /** | 26 /** |
36 * This class provides the method to start/stop ChildProcess called by native. | 27 * This class provides the method to start/stop ChildProcess called by native. |
37 * | 28 * |
38 * Note about threading. The threading here is complicated and not well document ed. | 29 * Note about threading. The threading here is complicated and not well document ed. |
39 * Code can run on these threads: UI, Launcher, async thread pool, binder, and o ne-off | 30 * Code can run on these threads: UI, Launcher, async thread pool, binder, and o ne-off |
40 * background threads. | 31 * background threads. |
41 */ | 32 */ |
42 public class ChildProcessLauncher { | 33 public class ChildProcessLauncher { |
43 private static final String TAG = "ChildProcLauncher"; | 34 private static final String TAG = "ChildProcLauncher"; |
44 | 35 |
45 private static class ChildConnectionAllocator { | |
46 // Connections to services. Indices of the array correspond to the servi ce numbers. | |
47 private final ChildProcessConnection[] mChildProcessConnections; | |
48 | |
49 // The list of free (not bound) service indices. | |
50 // SHOULD BE ACCESSED WITH mConnectionLock. | |
51 private final ArrayList<Integer> mFreeConnectionIndices; | |
52 private final Object mConnectionLock = new Object(); | |
53 | |
54 private final String mChildClassName; | |
55 private final boolean mInSandbox; | |
56 // Each Allocator keeps a queue for the pending spawn data. Once a conne ction is free, we | |
57 // dequeue the pending spawn data from the same allocator as the connect ion. | |
58 // SHOULD BE ACCESSED WITH mConnectionLock. | |
59 private final Queue<SpawnData> mPendingSpawnQueue = new LinkedList<>(); | |
60 | |
61 public ChildConnectionAllocator(boolean inSandbox, int numChildServices, | |
62 String serviceClassName) { | |
63 mChildProcessConnections = new ChildProcessConnectionImpl[numChildSe rvices]; | |
64 mFreeConnectionIndices = new ArrayList<Integer>(numChildServices); | |
65 for (int i = 0; i < numChildServices; i++) { | |
66 mFreeConnectionIndices.add(i); | |
67 } | |
68 mChildClassName = serviceClassName; | |
69 mInSandbox = inSandbox; | |
70 } | |
71 | |
72 // Allocate or enqueue. If there are no free slots, return null and enqu eue the spawn data. | |
73 public ChildProcessConnection allocate(SpawnData spawnData, | |
74 ChildProcessConnection.DeathCallback deathCallback, | |
75 Bundle childProcessCommonParameters) { | |
76 assert spawnData.isInSandbox() == mInSandbox; | |
77 synchronized (mConnectionLock) { | |
78 if (mFreeConnectionIndices.isEmpty()) { | |
79 Log.d(TAG, "Ran out of services to allocate."); | |
80 if (!spawnData.isForWarmUp()) { | |
81 mPendingSpawnQueue.add(spawnData); | |
82 } | |
83 return null; | |
84 } | |
85 int slot = mFreeConnectionIndices.remove(0); | |
86 assert mChildProcessConnections[slot] == null; | |
87 mChildProcessConnections[slot] = | |
88 new ChildProcessConnectionImpl(spawnData.getContext(), s lot, mInSandbox, | |
89 deathCallback, mChildClassName, childProcessComm onParameters, | |
90 spawnData.isAlwaysInForeground(), spawnData.getC reationParams()); | |
91 Log.d(TAG, "Allocator allocated a connection, sandbox: %b, slot: %d", mInSandbox, | |
92 slot); | |
93 return mChildProcessConnections[slot]; | |
94 } | |
95 } | |
96 | |
97 // Also return the first SpawnData in the pending queue, if any. | |
98 public SpawnData free(ChildProcessConnection connection) { | |
99 synchronized (mConnectionLock) { | |
100 int slot = connection.getServiceNumber(); | |
101 if (mChildProcessConnections[slot] != connection) { | |
102 int occupier = mChildProcessConnections[slot] == null | |
103 ? -1 : mChildProcessConnections[slot].getServiceNumb er(); | |
104 Log.e(TAG, "Unable to find connection to free in slot: %d " | |
105 + "already occupied by service: %d", slot, occupier) ; | |
106 assert false; | |
107 } else { | |
108 mChildProcessConnections[slot] = null; | |
109 assert !mFreeConnectionIndices.contains(slot); | |
110 mFreeConnectionIndices.add(slot); | |
111 Log.d(TAG, "Allocator freed a connection, sandbox: %b, slot: %d", mInSandbox, | |
112 slot); | |
113 } | |
114 return mPendingSpawnQueue.poll(); | |
115 } | |
116 } | |
117 | |
118 public boolean isFreeConnectionAvailable() { | |
119 synchronized (mConnectionLock) { | |
120 return !mFreeConnectionIndices.isEmpty(); | |
121 } | |
122 } | |
123 | |
124 /** @return the count of connections managed by the allocator */ | |
125 @VisibleForTesting | |
126 int allocatedConnectionsCountForTesting() { | |
127 return mChildProcessConnections.length - mFreeConnectionIndices.size (); | |
128 } | |
129 | |
130 @VisibleForTesting | |
131 ChildProcessConnection[] connectionArrayForTesting() { | |
132 return mChildProcessConnections; | |
133 } | |
134 | |
135 @VisibleForTesting | |
136 void enqueuePendingQueueForTesting(SpawnData spawnData) { | |
137 synchronized (mConnectionLock) { | |
138 mPendingSpawnQueue.add(spawnData); | |
139 } | |
140 } | |
141 | |
142 @VisibleForTesting | |
143 int pendingSpawnsCountForTesting() { | |
144 synchronized (mConnectionLock) { | |
145 return mPendingSpawnQueue.size(); | |
146 } | |
147 } | |
148 } | |
149 | |
150 private static class SpawnData { | |
151 private final boolean mForWarmup; // Do not queue up if failed. | |
152 private final Context mContext; | |
153 private final String[] mCommandLine; | |
154 private final int mChildProcessId; | |
155 private final FileDescriptorInfo[] mFilesToBeMapped; | |
156 private final LaunchCallback mLaunchCallback; | |
157 private final IBinder mChildProcessCallback; | |
158 private final boolean mInSandbox; | |
159 private final boolean mAlwaysInForeground; | |
160 private final ChildProcessCreationParams mCreationParams; | |
161 | |
162 private SpawnData(boolean forWarmUp, Context context, String[] commandLi ne, | |
163 int childProcessId, FileDescriptorInfo[] filesToBeMapped, | |
164 LaunchCallback launchCallback, IBinder childProcessCallback, boo lean inSandbox, | |
165 boolean alwaysInForeground, ChildProcessCreationParams creationP arams) { | |
166 mForWarmup = forWarmUp; | |
167 mContext = context; | |
168 mCommandLine = commandLine; | |
169 mChildProcessId = childProcessId; | |
170 mFilesToBeMapped = filesToBeMapped; | |
171 mLaunchCallback = launchCallback; | |
172 mChildProcessCallback = childProcessCallback; | |
173 mInSandbox = inSandbox; | |
174 mAlwaysInForeground = alwaysInForeground; | |
175 mCreationParams = creationParams; | |
176 } | |
177 | |
178 private boolean isForWarmUp() { | |
179 return mForWarmup; | |
180 } | |
181 private Context getContext() { | |
182 return mContext; | |
183 } | |
184 private String[] getCommandLine() { | |
185 return mCommandLine; | |
186 } | |
187 private int getChildProcessId() { | |
188 return mChildProcessId; | |
189 } | |
190 private FileDescriptorInfo[] getFilesToBeMapped() { | |
191 return mFilesToBeMapped; | |
192 } | |
193 private LaunchCallback getLaunchCallback() { | |
194 return mLaunchCallback; | |
195 } | |
196 private IBinder getChildProcessCallback() { | |
197 return mChildProcessCallback; | |
198 } | |
199 private boolean isInSandbox() { | |
200 return mInSandbox; | |
201 } | |
202 private boolean isAlwaysInForeground() { | |
203 return mAlwaysInForeground; | |
204 } | |
205 private ChildProcessCreationParams getCreationParams() { | |
206 return mCreationParams; | |
207 } | |
208 } | |
209 | |
210 /** | 36 /** |
211 * Implemented by ChildProcessLauncherHelper. | 37 * Implemented by ChildProcessLauncherHelper. |
212 */ | 38 */ |
213 public interface LaunchCallback { void onChildProcessStarted(int pid); } | 39 public interface LaunchCallback { void onChildProcessStarted(int pid); } |
214 | 40 |
215 // Service class for child process. | |
216 // Map from package name to ChildConnectionAllocator. | |
217 private static Map<String, ChildConnectionAllocator> sSandboxedChildConnecti onAllocatorMap; | |
218 // As the default value it uses PrivilegedProcessService0. | |
219 private static ChildConnectionAllocator sPrivilegedChildConnectionAllocator; | |
220 | |
221 private static final boolean SPARE_CONNECTION_ALWAYS_IN_FOREGROUND = false; | 41 private static final boolean SPARE_CONNECTION_ALWAYS_IN_FOREGROUND = false; |
222 | 42 |
223 private static final String NUM_SANDBOXED_SERVICES_KEY = | |
224 "org.chromium.content.browser.NUM_SANDBOXED_SERVICES"; | |
225 private static final String NUM_PRIVILEGED_SERVICES_KEY = | |
226 "org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"; | |
227 private static final String SANDBOXED_SERVICES_NAME_KEY = | |
228 "org.chromium.content.browser.SANDBOXED_SERVICES_NAME"; | |
229 | |
230 // Used by tests to override the default sandboxed service settings. | |
231 private static int sSandboxedServicesCountForTesting = -1; | |
232 private static String sSandboxedServicesNameForTesting; | |
233 | |
234 @VisibleForTesting | |
235 public static void setSanboxServicesSettingsForTesting(int serviceCount, Str ing serviceName) { | |
236 sSandboxedServicesCountForTesting = serviceCount; | |
237 sSandboxedServicesNameForTesting = serviceName; | |
238 } | |
239 | |
240 static int getNumberOfServices(Context context, boolean inSandbox, String pa ckageName) { | |
241 int numServices = -1; | |
242 if (inSandbox && sSandboxedServicesCountForTesting != -1) { | |
243 numServices = sSandboxedServicesCountForTesting; | |
244 } else { | |
245 try { | |
246 PackageManager packageManager = context.getPackageManager(); | |
247 ApplicationInfo appInfo = packageManager.getApplicationInfo(pack ageName, | |
248 PackageManager.GET_META_DATA); | |
249 if (appInfo.metaData != null) { | |
250 numServices = appInfo.metaData.getInt(inSandbox | |
251 ? NUM_SANDBOXED_SERVICES_KEY : NUM_PRIVILEGED_SERVIC ES_KEY, -1); | |
252 } | |
253 } catch (PackageManager.NameNotFoundException e) { | |
254 throw new RuntimeException("Could not get application info"); | |
255 } | |
256 } | |
257 if (numServices < 0) { | |
258 throw new RuntimeException("Illegal meta data value for number of ch ild services"); | |
259 } | |
260 return numServices; | |
261 } | |
262 | |
263 private static String getClassNameOfService(Context context, boolean inSandb ox, | |
264 String packageName) { | |
265 if (!inSandbox) { | |
266 return PrivilegedProcessService.class.getName(); | |
267 } | |
268 | |
269 if (!TextUtils.isEmpty(sSandboxedServicesNameForTesting)) { | |
270 return sSandboxedServicesNameForTesting; | |
271 } | |
272 | |
273 String serviceName = null; | |
274 try { | |
275 PackageManager packageManager = context.getPackageManager(); | |
276 ApplicationInfo appInfo = | |
277 packageManager.getApplicationInfo(packageName, PackageManage r.GET_META_DATA); | |
278 if (appInfo.metaData != null) { | |
279 serviceName = appInfo.metaData.getString(SANDBOXED_SERVICES_NAME _KEY); | |
280 } | |
281 } catch (PackageManager.NameNotFoundException e) { | |
282 throw new RuntimeException("Could not get application info."); | |
283 } | |
284 | |
285 if (serviceName != null) { | |
286 // Check that the service exists. | |
287 try { | |
288 PackageManager packageManager = context.getPackageManager(); | |
289 // PackageManager#getServiceInfo() throws an exception if the se rvice does not | |
290 // exist. | |
291 packageManager.getServiceInfo(new ComponentName(packageName, ser viceName + "0"), 0); | |
292 return serviceName; | |
293 } catch (PackageManager.NameNotFoundException e) { | |
294 throw new RuntimeException( | |
295 "Illegal meta data value: the child service doesn't exis t"); | |
296 } | |
297 } | |
298 return SandboxedProcessService.class.getName(); | |
299 } | |
300 | |
301 private static void initConnectionAllocatorsIfNecessary( | |
302 Context context, boolean inSandbox, String packageName) { | |
303 // TODO(mariakhomenko): Uses an Object to lock the access. | |
304 synchronized (ChildProcessLauncher.class) { | |
305 if (inSandbox) { | |
306 if (sSandboxedChildConnectionAllocatorMap == null) { | |
307 sSandboxedChildConnectionAllocatorMap = | |
308 new ConcurrentHashMap<String, ChildConnectionAllocat or>(); | |
309 } | |
310 if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageNa me)) { | |
311 Log.w(TAG, "Create a new ChildConnectionAllocator with packa ge name = %s," | |
312 + " inSandbox = true", | |
313 packageName); | |
314 sSandboxedChildConnectionAllocatorMap.put(packageName, | |
315 new ChildConnectionAllocator(true, | |
316 getNumberOfServices(context, true, packageNa me), | |
317 getClassNameOfService(context, true, package Name))); | |
318 } | |
319 } else if (sPrivilegedChildConnectionAllocator == null) { | |
320 sPrivilegedChildConnectionAllocator = new ChildConnectionAllocat or(false, | |
321 getNumberOfServices(context, false, packageName), | |
322 getClassNameOfService(context, false, packageName)); | |
323 } | |
324 // TODO(pkotwicz|hanxi): Figure out when old allocators should be re moved from | |
325 // {@code sSandboxedChildConnectionAllocatorMap}. | |
326 } | |
327 } | |
328 | |
329 /** | |
330 * Note: please make sure that the Allocator has been initialized before cal ling this function. | |
331 * Otherwise, always calls {@link initConnectionAllocatorsIfNecessary} first . | |
332 */ | |
333 private static ChildConnectionAllocator getConnectionAllocator( | |
334 String packageName, boolean inSandbox) { | |
335 if (!inSandbox) { | |
336 return sPrivilegedChildConnectionAllocator; | |
337 } | |
338 return sSandboxedChildConnectionAllocatorMap.get(packageName); | |
339 } | |
340 | |
341 private static ChildConnectionAllocator getAllocatorForTesting( | |
342 Context context, String packageName, boolean inSandbox) { | |
343 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName); | |
344 return getConnectionAllocator(packageName, inSandbox); | |
345 } | |
346 | |
347 private static ChildProcessConnection allocateConnection( | 43 private static ChildProcessConnection allocateConnection( |
348 SpawnData spawnData, Bundle childProcessCommonParams) { | 44 ChildSpawnData spawnData, Bundle childProcessCommonParams, boolean f orWarmUp) { |
349 ChildProcessConnection.DeathCallback deathCallback = | 45 ChildProcessConnection.DeathCallback deathCallback = |
350 new ChildProcessConnection.DeathCallback() { | 46 new ChildProcessConnection.DeathCallback() { |
351 @Override | 47 @Override |
352 public void onChildProcessDied(ChildProcessConnection connec tion) { | 48 public void onChildProcessDied(ChildProcessConnection connec tion) { |
353 if (connection.getPid() != 0) { | 49 if (connection.getPid() != 0) { |
354 stop(connection.getPid()); | 50 stop(connection.getPid()); |
355 } else { | 51 } else { |
356 freeConnection(connection); | 52 freeConnection(connection); |
357 } | 53 } |
358 } | 54 } |
359 }; | 55 }; |
360 final ChildProcessCreationParams creationParams = spawnData.getCreationP arams(); | 56 final ChildProcessCreationParams creationParams = spawnData.getCreationP arams(); |
361 final Context context = spawnData.getContext(); | 57 final Context context = spawnData.getContext(); |
362 final boolean inSandbox = spawnData.isInSandbox(); | 58 final boolean inSandbox = spawnData.isInSandbox(); |
363 String packageName = | 59 String packageName = |
364 creationParams != null ? creationParams.getPackageName() : conte xt.getPackageName(); | 60 creationParams != null ? creationParams.getPackageName() : conte xt.getPackageName(); |
365 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName); | 61 return ChildConnectionAllocator.getAllocator(context, packageName, inSan dbox) |
366 return getConnectionAllocator(packageName, inSandbox) | 62 .allocate(spawnData, deathCallback, childProcessCommonParams, !f orWarmUp); |
367 .allocate(spawnData, deathCallback, childProcessCommonParams); | |
368 } | 63 } |
369 | 64 |
370 private static boolean sLinkerInitialized; | 65 private static boolean sLinkerInitialized; |
371 private static long sLinkerLoadAddress; | 66 private static long sLinkerLoadAddress; |
372 | 67 |
373 private static ChromiumLinkerParams getLinkerParamsForNewConnection() { | 68 private static ChromiumLinkerParams getLinkerParamsForNewConnection() { |
374 if (!sLinkerInitialized) { | 69 if (!sLinkerInitialized) { |
375 if (Linker.isUsed()) { | 70 if (Linker.isUsed()) { |
376 sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress(); | 71 sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress(); |
377 if (sLinkerLoadAddress == 0) { | 72 if (sLinkerLoadAddress == 0) { |
(...skipping 21 matching lines...) Expand all Loading... | |
399 | 94 |
400 private static Bundle createCommonParamsBundle(ChildProcessCreationParams pa rams) { | 95 private static Bundle createCommonParamsBundle(ChildProcessCreationParams pa rams) { |
401 Bundle commonParams = new Bundle(); | 96 Bundle commonParams = new Bundle(); |
402 commonParams.putParcelable( | 97 commonParams.putParcelable( |
403 ChildProcessConstants.EXTRA_LINKER_PARAMS, getLinkerParamsForNew Connection()); | 98 ChildProcessConstants.EXTRA_LINKER_PARAMS, getLinkerParamsForNew Connection()); |
404 final boolean bindToCallerCheck = params == null ? false : params.getBin dToCallerCheck(); | 99 final boolean bindToCallerCheck = params == null ? false : params.getBin dToCallerCheck(); |
405 commonParams.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER, bind ToCallerCheck); | 100 commonParams.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER, bind ToCallerCheck); |
406 return commonParams; | 101 return commonParams; |
407 } | 102 } |
408 | 103 |
409 private static ChildProcessConnection allocateBoundConnection( | 104 private static ChildProcessConnection allocateBoundConnection(ChildSpawnData spawnData, |
410 SpawnData spawnData, ChildProcessConnection.StartCallback startCallb ack) { | 105 ChildProcessConnection.StartCallback startCallback, boolean forWarmU p) { |
411 final Context context = spawnData.getContext(); | 106 final Context context = spawnData.getContext(); |
412 final boolean inSandbox = spawnData.isInSandbox(); | 107 final boolean inSandbox = spawnData.isInSandbox(); |
413 final ChildProcessCreationParams creationParams = spawnData.getCreationP arams(); | 108 final ChildProcessCreationParams creationParams = spawnData.getCreationP arams(); |
109 | |
414 ChildProcessConnection connection = allocateConnection( | 110 ChildProcessConnection connection = allocateConnection( |
415 spawnData, createCommonParamsBundle(spawnData.getCreationParams( ))); | 111 spawnData, createCommonParamsBundle(spawnData.getCreationParams( )), forWarmUp); |
416 if (connection != null) { | 112 if (connection != null) { |
417 connection.start(startCallback); | 113 connection.start(startCallback); |
418 | 114 |
419 String packageName = creationParams != null ? creationParams.getPack ageName() | 115 String packageName = creationParams != null ? creationParams.getPack ageName() |
420 : context.getPackageName (); | 116 : context.getPackageName (); |
421 if (inSandbox | 117 if (inSandbox |
422 && !getConnectionAllocator(packageName, inSandbox) | 118 && !ChildConnectionAllocator.getAllocator(context, packageNa me, inSandbox) |
423 .isFreeConnectionAvailable()) { | 119 .isFreeConnectionAvailable()) { |
424 // Proactively releases all the moderate bindings once all the s andboxed services | 120 // Proactively releases all the moderate bindings once all the s andboxed services |
425 // are allocated, which will be very likely to have some of them killed by OOM | 121 // are allocated, which will be very likely to have some of them killed by OOM |
426 // killer. | 122 // killer. |
427 sBindingManager.releaseAllModerateBindings(); | 123 sBindingManager.releaseAllModerateBindings(); |
428 } | 124 } |
429 } | 125 } |
430 return connection; | 126 return connection; |
431 } | 127 } |
432 | 128 |
433 private static final long FREE_CONNECTION_DELAY_MILLIS = 1; | 129 private static final long FREE_CONNECTION_DELAY_MILLIS = 1; |
434 | 130 |
435 private static void freeConnection(ChildProcessConnection connection) { | 131 private static void freeConnection(ChildProcessConnection connection) { |
436 synchronized (sSpareConnectionLock) { | 132 synchronized (sSpareConnectionLock) { |
437 if (connection.equals(sSpareSandboxedConnection)) sSpareSandboxedCon nection = null; | 133 if (connection.equals(sSpareSandboxedConnection)) sSpareSandboxedCon nection = null; |
438 } | 134 } |
439 | 135 |
440 // Freeing a service should be delayed. This is so that we avoid immedia tely reusing the | 136 // Freeing a service should be delayed. This is so that we avoid immedia tely reusing the |
441 // freed service (see http://crbug.com/164069): the framework might keep a service process | 137 // freed service (see http://crbug.com/164069): the framework might keep a service process |
442 // alive when it's been unbound for a short time. If a new connection to the same service | 138 // alive when it's been unbound for a short time. If a new connection to the same service |
443 // is bound at that point, the process is reused and bad things happen ( mostly static | 139 // is bound at that point, the process is reused and bad things happen ( mostly static |
444 // variables are set when we don't expect them to). | 140 // variables are set when we don't expect them to). |
445 final ChildProcessConnection conn = connection; | 141 final ChildProcessConnection conn = connection; |
446 ThreadUtils.postOnUiThreadDelayed(new Runnable() { | 142 ThreadUtils.postOnUiThreadDelayed(new Runnable() { |
447 @Override | 143 @Override |
448 public void run() { | 144 public void run() { |
449 final SpawnData pendingSpawn = freeConnectionAndDequeuePending(c onn); | 145 final ChildSpawnData pendingSpawn = freeConnectionAndDequeuePend ing(conn); |
450 if (pendingSpawn != null) { | 146 if (pendingSpawn != null) { |
451 LauncherThread.post(new Runnable() { | 147 LauncherThread.post(new Runnable() { |
452 @Override | 148 @Override |
453 public void run() { | 149 public void run() { |
454 startInternal(pendingSpawn.getContext(), pendingSpaw n.getCommandLine(), | 150 startInternal(pendingSpawn.getContext(), pendingSpaw n.getCommandLine(), |
455 pendingSpawn.getChildProcessId(), | 151 pendingSpawn.getChildProcessId(), |
456 pendingSpawn.getFilesToBeMapped(), | 152 pendingSpawn.getFilesToBeMapped(), |
457 pendingSpawn.getLaunchCallback(), | 153 pendingSpawn.getLaunchCallback(), |
458 pendingSpawn.getChildProcessCallback(), | 154 pendingSpawn.getChildProcessCallback(), |
459 pendingSpawn.isInSandbox(), pendingSpawn.isA lwaysInForeground(), | 155 pendingSpawn.isInSandbox(), pendingSpawn.isA lwaysInForeground(), |
460 pendingSpawn.getCreationParams()); | 156 pendingSpawn.getCreationParams()); |
461 } | 157 } |
462 }); | 158 }); |
463 } | 159 } |
464 } | 160 } |
465 }, FREE_CONNECTION_DELAY_MILLIS); | 161 }, FREE_CONNECTION_DELAY_MILLIS); |
466 } | 162 } |
467 | 163 |
468 private static SpawnData freeConnectionAndDequeuePending(ChildProcessConnect ion conn) { | 164 private static ChildSpawnData freeConnectionAndDequeuePending(ChildProcessCo nnection conn) { |
469 ChildConnectionAllocator allocator = getConnectionAllocator( | 165 // TODO(jcivelli): it should be safe to pass a null Context here as it i s used to initialize |
boliu
2017/04/12 00:20:16
assert in getAllocator that the cases where a new
Jay Civelli
2017/04/12 00:47:15
We could, but you'll get an NPE with a nice stack-
| |
470 conn.getPackageName(), conn.isInSandbox()); | 166 // the ChildConnectionAllocator object and if we are freeing a connectio n, we must have |
167 // allocated one previously guaranteeing it is already initialized. | |
168 // When we consolidate ChildProcessLauncher and ChildProcessLauncherHelp er, we'll have a | |
169 // context around that we can pass in there. | |
170 ChildConnectionAllocator allocator = ChildConnectionAllocator.getAllocat or( | |
171 null /* context */, conn.getPackageName(), conn.isInSandbox()); | |
471 assert allocator != null; | 172 assert allocator != null; |
472 return allocator.free(conn); | 173 return allocator.free(conn); |
473 } | 174 } |
474 | 175 |
475 // Represents an invalid process handle; same as base/process/process.h kNul lProcessHandle. | 176 // Represents an invalid process handle; same as base/process/process.h kNul lProcessHandle. |
476 private static final int NULL_PROCESS_HANDLE = 0; | 177 private static final int NULL_PROCESS_HANDLE = 0; |
477 | 178 |
478 // Map from pid to ChildService connection. | 179 // Map from pid to ChildService connection. |
479 private static Map<Integer, ChildProcessConnection> sServiceMap = | 180 private static Map<Integer, ChildProcessConnection> sServiceMap = |
480 new ConcurrentHashMap<Integer, ChildProcessConnection>(); | 181 new ConcurrentHashMap<Integer, ChildProcessConnection>(); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
526 * Starts moderate binding management. | 227 * Starts moderate binding management. |
527 * Note: WebAPKs and non WebAPKs share the same moderate binding pool, so th e size of the | 228 * Note: WebAPKs and non WebAPKs share the same moderate binding pool, so th e size of the |
528 * shared moderate binding pool is always set based on the number of sandbox es processes | 229 * shared moderate binding pool is always set based on the number of sandbox es processes |
529 * used by Chrome. | 230 * used by Chrome. |
530 * @param context Android's context. | 231 * @param context Android's context. |
531 * @param moderateBindingTillBackgrounded true if the BindingManager should add a moderate | 232 * @param moderateBindingTillBackgrounded true if the BindingManager should add a moderate |
532 * binding to a render process when it is created and remove the moderate bi nding when Chrome is | 233 * binding to a render process when it is created and remove the moderate bi nding when Chrome is |
533 * sent to the background. | 234 * sent to the background. |
534 */ | 235 */ |
535 public static void startModerateBindingManagement(Context context) { | 236 public static void startModerateBindingManagement(Context context) { |
536 sBindingManager.startModerateBindingManagement( | 237 sBindingManager.startModerateBindingManagement(context, |
537 context, getNumberOfServices(context, true, context.getPackageNa me())); | 238 ChildConnectionAllocator.getNumberOfServices( |
239 context, true, context.getPackageName())); | |
538 } | 240 } |
539 | 241 |
540 /** | 242 /** |
541 * Called when the embedding application is brought to foreground. | 243 * Called when the embedding application is brought to foreground. |
542 */ | 244 */ |
543 public static void onBroughtToForeground() { | 245 public static void onBroughtToForeground() { |
544 sApplicationInForeground = true; | 246 sApplicationInForeground = true; |
545 sBindingManager.onBroughtToForeground(); | 247 sBindingManager.onBroughtToForeground(); |
546 } | 248 } |
547 | 249 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
580 @Override | 282 @Override |
581 public void onChildStartFailed() { | 283 public void onChildStartFailed() { |
582 Log.e(TAG, "Failed to warm up the spare sandbox service"); | 284 Log.e(TAG, "Failed to warm up the spare sandbox service"); |
583 synchronized (sSpareConnectionLock) { | 285 synchronized (sSpareConnectionLock) { |
584 sSpareSandboxedConnection = null; | 286 sSpareSandboxedConnection = null; |
585 sSpareConnectionStarting = false; | 287 sSpareConnectionStarting = false; |
586 sSpareConnectionLock.notify(); | 288 sSpareConnectionLock.notify(); |
587 } | 289 } |
588 } | 290 } |
589 }; | 291 }; |
590 SpawnData spawnData = new SpawnData(true /* forWarmUp*/, context, | 292 ChildSpawnData spawnData = new ChildSpawnData(context, |
591 null /* commandLine */, -1 /* child process id * /, | 293 null /* commandLine */, -1 /* child process id * /, |
592 null /* filesToBeMapped */, null /* launchCallba ck */, | 294 null /* filesToBeMapped */, null /* launchCallba ck */, |
593 null /* child process callback */, true /* inSan dbox */, | 295 null /* child process callback */, true /* inSan dbox */, |
594 SPARE_CONNECTION_ALWAYS_IN_FOREGROUND, params); | 296 SPARE_CONNECTION_ALWAYS_IN_FOREGROUND, params); |
595 sSpareSandboxedConnection = | 297 sSpareSandboxedConnection = allocateBoundConnection( |
596 allocateBoundConnection(spawnData, startCallback ); | 298 spawnData, startCallback, true /* forWarmUp */); |
597 } | 299 } |
598 } | 300 } |
599 } | 301 } |
600 }); | 302 }); |
601 } | 303 } |
602 | 304 |
603 /** | 305 /** |
604 * Spawns and connects to a child process. May be called on any thread. It w ill not block, but | 306 * Spawns and connects to a child process. May be called on any thread. It w ill not block, but |
605 * will instead callback to {@link #nativeOnChildProcessStarted} when the co nnection is | 307 * will instead callback to {@link #nativeOnChildProcessStarted} when the co nnection is |
606 * established. Note this callback will not necessarily be from the same thr ead (currently it | 308 * established. Note this callback will not necessarily be from the same thr ead (currently it |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
700 // so a new bound connection will be all ocated. | 402 // so a new bound connection will be all ocated. |
701 startInternal(context, commandLine, chil dProcessId, | 403 startInternal(context, commandLine, chil dProcessId, |
702 filesToBeMapped, launchCallback, | 404 filesToBeMapped, launchCallback, |
703 childProcessCallback, inSandbox, alwaysInForeground, | 405 childProcessCallback, inSandbox, alwaysInForeground, |
704 creationParams); | 406 creationParams); |
705 } | 407 } |
706 }); | 408 }); |
707 } | 409 } |
708 }; | 410 }; |
709 | 411 |
710 SpawnData spawnData = new SpawnData(false /* forWarmUp */, conte xt, commandLine, | 412 ChildSpawnData spawnData = new ChildSpawnData(context, commandLi ne, childProcessId, |
711 childProcessId, filesToBeMapped, launchCallback, childPr ocessCallback, | 413 filesToBeMapped, launchCallback, childProcessCallback, i nSandbox, |
712 inSandbox, alwaysInForeground, creationParams); | 414 alwaysInForeground, creationParams); |
713 allocatedConnection = allocateBoundConnection(spawnData, startCa llback); | 415 allocatedConnection = |
416 allocateBoundConnection(spawnData, startCallback, false /* forWarmUp */); | |
714 if (allocatedConnection == null) { | 417 if (allocatedConnection == null) { |
715 return null; | 418 return null; |
716 } | 419 } |
717 } | 420 } |
718 | 421 |
719 Log.d(TAG, "Setting up connection to process: slot=%d", | 422 Log.d(TAG, "Setting up connection to process: slot=%d", |
720 allocatedConnection.getServiceNumber()); | 423 allocatedConnection.getServiceNumber()); |
721 triggerConnectionSetup(allocatedConnection, commandLine, childProces sId, | 424 triggerConnectionSetup(allocatedConnection, commandLine, childProces sId, |
722 filesToBeMapped, childProcessCallback, launchCallback); | 425 filesToBeMapped, childProcessCallback, launchCallback); |
723 return allocatedConnection; | 426 return allocatedConnection; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
791 FileDescriptorInfo[] filesToMap, ChildProcessCreationParams params) { | 494 FileDescriptorInfo[] filesToMap, ChildProcessCreationParams params) { |
792 return startInternal(context, commandLine, 0 /* childProcessId */, files ToMap, | 495 return startInternal(context, commandLine, 0 /* childProcessId */, files ToMap, |
793 null /* launchCallback */, null /* childProcessCallback */, true /* inSandbox */, | 496 null /* launchCallback */, null /* childProcessCallback */, true /* inSandbox */, |
794 false /* alwaysInForeground */, params); | 497 false /* alwaysInForeground */, params); |
795 } | 498 } |
796 | 499 |
797 @VisibleForTesting | 500 @VisibleForTesting |
798 static ChildProcessConnection allocateBoundConnectionForTesting(Context cont ext, | 501 static ChildProcessConnection allocateBoundConnectionForTesting(Context cont ext, |
799 ChildProcessCreationParams creationParams) { | 502 ChildProcessCreationParams creationParams) { |
800 return allocateBoundConnection( | 503 return allocateBoundConnection( |
801 new SpawnData(false /* forWarmUp */, context, null /* commandLin e */, | 504 new ChildSpawnData(context, null /* commandLine */, 0 /* childPr ocessId */, |
802 0 /* childProcessId */, null /* filesToBeMapped */, | 505 null /* filesToBeMapped */, null /* LaunchCallback */, |
803 null /* LaunchCallback */, null /* childProcessCallback */, | 506 null /* childProcessCallback */, true /* inSandbox */, |
804 true /* inSandbox */, false /* alwaysInForeground */, cr eationParams), | 507 false /* alwaysInForeground */, creationParams), |
805 null /* startCallback */); | 508 null /* startCallback */, false /* forWarmUp */); |
806 } | 509 } |
807 | 510 |
808 @VisibleForTesting | 511 @VisibleForTesting |
809 static ChildProcessConnection allocateConnectionForTesting( | 512 static ChildProcessConnection allocateConnectionForTesting( |
810 Context context, ChildProcessCreationParams creationParams) { | 513 Context context, ChildProcessCreationParams creationParams) { |
811 return allocateConnection( | 514 return allocateConnection( |
812 new SpawnData(false /* forWarmUp */, context, null /* commandLin e */, | 515 new ChildSpawnData(context, null /* commandLine */, 0 /* childPr ocessId */, |
813 0 /* childProcessId */, null /* filesToBeMapped */, | 516 null /* filesToBeMapped */, null /* launchCallback */, |
814 null /* launchCallback */, null /* childProcessCallback */, | 517 null /* childProcessCallback */, true /* inSandbox */, |
815 true /* inSandbox */, false /* alwaysInForeground */, cr eationParams), | 518 false /* alwaysInForeground */, creationParams), |
816 createCommonParamsBundle(creationParams)); | 519 createCommonParamsBundle(creationParams), false /* forWarmUp */) ; |
817 } | 520 } |
818 | 521 |
819 /** | 522 /** |
820 * Queue up a spawn requests for testing. | 523 * Queue up a spawn requests for testing. |
821 */ | 524 */ |
822 @VisibleForTesting | 525 @VisibleForTesting |
823 static void enqueuePendingSpawnForTesting(Context context, String[] commandL ine, | 526 static void enqueuePendingSpawnForTesting(Context context, String[] commandL ine, |
824 ChildProcessCreationParams creationParams, boolean inSandbox) { | 527 ChildProcessCreationParams creationParams, boolean inSandbox) { |
825 String packageName = creationParams != null ? creationParams.getPackageN ame() | 528 String packageName = creationParams != null ? creationParams.getPackageN ame() |
826 : context.getPackageName(); | 529 : context.getPackageName(); |
827 ChildConnectionAllocator allocator = | 530 ChildConnectionAllocator allocator = |
828 getAllocatorForTesting(context, packageName, inSandbox); | 531 ChildConnectionAllocator.getAllocator(context, packageName, inSa ndbox); |
829 allocator.enqueuePendingQueueForTesting(new SpawnData(false /* forWarmUp */, context, | 532 allocator.enqueuePendingQueueForTesting(new ChildSpawnData(context, comm andLine, |
830 commandLine, 1 /* childProcessId */, new FileDescriptorInfo[0], | 533 1 /* childProcessId */, new FileDescriptorInfo[0], null /* launc hCallback */, |
831 null /* launchCallback */, null /* childProcessCallback */, true /* inSandbox */, | 534 null /* childProcessCallback */, true /* inSandbox */, |
832 false /* alwaysInForeground */, creationParams)); | 535 false /* alwaysInForeground */, creationParams)); |
833 } | 536 } |
834 | 537 |
835 /** | 538 /** |
836 * @return the number of sandboxed connections of given {@link packageName} managed by the | 539 * @return the number of sandboxed connections of given {@link packageName} managed by the |
837 * allocator. | 540 * allocator. |
838 */ | 541 */ |
839 @VisibleForTesting | 542 @VisibleForTesting |
840 static int allocatedSandboxedConnectionsCountForTesting(Context context, Str ing packageName) { | 543 static int allocatedSandboxedConnectionsCountForTesting(Context context, Str ing packageName) { |
841 initConnectionAllocatorsIfNecessary(context, true, packageName); | 544 return ChildConnectionAllocator.getAllocator(context, packageName, true /*isSandboxed */) |
842 return sSandboxedChildConnectionAllocatorMap.get(packageName) | |
843 .allocatedConnectionsCountForTesting(); | 545 .allocatedConnectionsCountForTesting(); |
844 } | 546 } |
845 | 547 |
846 /** | 548 /** |
847 * @return gets the service connection array for a specific package name. | 549 * @return gets the service connection array for a specific package name. |
848 */ | 550 */ |
849 @VisibleForTesting | 551 @VisibleForTesting |
850 static ChildProcessConnection[] getSandboxedConnectionArrayForTesting(String packageName) { | 552 static ChildProcessConnection[] getSandboxedConnectionArrayForTesting( |
851 return sSandboxedChildConnectionAllocatorMap.get(packageName).connection ArrayForTesting(); | 553 Context context, String packageName) { |
554 return ChildConnectionAllocator.getAllocator(context, packageName, true /*isSandboxed */) | |
555 .connectionArrayForTesting(); | |
852 } | 556 } |
853 | 557 |
854 /** @return the count of services set up and working */ | 558 /** @return the count of services set up and working */ |
855 @VisibleForTesting | 559 @VisibleForTesting |
856 static int connectedServicesCountForTesting() { | 560 static int connectedServicesCountForTesting() { |
857 return sServiceMap.size(); | 561 return sServiceMap.size(); |
858 } | 562 } |
859 | 563 |
860 /** | 564 /** |
861 * @param context The context. | 565 * @param context The context. |
862 * @param packageName The package name of the {@link ChildProcessAlocator}. | 566 * @param packageName The package name of the {@link ChildProcessAlocator}. |
863 * @param inSandbox Whether the connection is sandboxed. | 567 * @param inSandbox Whether the connection is sandboxed. |
864 * @return the count of pending spawns in the queue. | 568 * @return the count of pending spawns in the queue. |
865 */ | 569 */ |
866 @VisibleForTesting | 570 @VisibleForTesting |
867 static int pendingSpawnsCountForTesting( | 571 static int pendingSpawnsCountForTesting( |
868 Context context, String packageName, boolean inSandbox) { | 572 Context context, String packageName, boolean inSandbox) { |
869 ChildConnectionAllocator allocator = | 573 return ChildConnectionAllocator.getAllocator(context, packageName, inSan dbox) |
870 getAllocatorForTesting(context, packageName, inSandbox); | 574 .pendingSpawnsCountForTesting(); |
871 return allocator.pendingSpawnsCountForTesting(); | |
872 } | 575 } |
873 | 576 |
874 /** | 577 /** |
875 * Kills the child process for testing. | 578 * Kills the child process for testing. |
876 * @return true iff the process was killed as expected | 579 * @return true iff the process was killed as expected |
877 */ | 580 */ |
878 @VisibleForTesting | 581 @VisibleForTesting |
879 public static boolean crashProcessForTesting(int pid) { | 582 public static boolean crashProcessForTesting(int pid) { |
880 if (sServiceMap.get(pid) == null) return false; | 583 if (sServiceMap.get(pid) == null) return false; |
881 | 584 |
882 try { | 585 try { |
883 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); | 586 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); |
884 } catch (RemoteException ex) { | 587 } catch (RemoteException ex) { |
885 return false; | 588 return false; |
886 } | 589 } |
887 | 590 |
888 return true; | 591 return true; |
889 } | 592 } |
890 } | 593 } |
OLD | NEW |