OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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.os.Handler; | 11 import android.os.Handler; |
12 import android.os.IBinder; | 12 import android.os.IBinder; |
13 import android.os.Looper; | 13 import android.os.Looper; |
14 import android.os.Message; | 14 import android.os.Message; |
15 import android.os.Messenger; | 15 import android.os.Messenger; |
16 import android.os.RemoteException; | 16 import android.os.RemoteException; |
17 import android.support.test.InstrumentationRegistry; | |
17 import android.support.test.filters.MediumTest; | 18 import android.support.test.filters.MediumTest; |
18 import android.test.InstrumentationTestCase; | 19 |
20 import org.junit.Assert; | |
21 import org.junit.Rule; | |
22 import org.junit.Test; | |
23 import org.junit.runner.RunWith; | |
19 | 24 |
20 import org.chromium.base.BaseSwitches; | 25 import org.chromium.base.BaseSwitches; |
21 import org.chromium.base.library_loader.LibraryLoader; | 26 import org.chromium.base.library_loader.LibraryLoader; |
22 import org.chromium.base.library_loader.LibraryProcessType; | 27 import org.chromium.base.library_loader.LibraryProcessType; |
28 import org.chromium.base.library_loader.ProcessInitException; | |
23 import org.chromium.base.process_launcher.ChildProcessCreationParams; | 29 import org.chromium.base.process_launcher.ChildProcessCreationParams; |
24 import org.chromium.base.process_launcher.FileDescriptorInfo; | 30 import org.chromium.base.process_launcher.FileDescriptorInfo; |
25 import org.chromium.base.test.util.CommandLineFlags; | 31 import org.chromium.base.test.BaseJUnit4ClassRunner; |
32 import org.chromium.base.test.util.CommandLineTestRule; | |
26 import org.chromium.base.test.util.Feature; | 33 import org.chromium.base.test.util.Feature; |
27 import org.chromium.content.browser.test.util.Criteria; | 34 import org.chromium.content.browser.test.util.Criteria; |
28 import org.chromium.content.browser.test.util.CriteriaHelper; | 35 import org.chromium.content.browser.test.util.CriteriaHelper; |
29 import org.chromium.content.common.ContentSwitches; | 36 import org.chromium.content.common.ContentSwitches; |
30 import org.chromium.content_shell_apk.ChildProcessLauncherTestHelperService; | 37 import org.chromium.content_shell_apk.ChildProcessLauncherTestHelperService; |
31 | 38 |
32 import java.util.concurrent.Callable; | 39 import java.util.concurrent.Callable; |
33 | 40 |
34 /** | 41 /** |
35 * Instrumentation tests for ChildProcessLauncher. | 42 * Instrumentation tests for ChildProcessLauncher. |
36 */ | 43 */ |
37 public class ChildProcessLauncherTest extends InstrumentationTestCase { | 44 @RunWith(BaseJUnit4ClassRunner.class) |
45 public class ChildProcessLauncherTest { | |
38 // Pseudo command line arguments to instruct the child process to wait until being killed. | 46 // Pseudo command line arguments to instruct the child process to wait until being killed. |
39 // Allowing the process to continue would lead to a crash when attempting to initialize IPC | 47 // Allowing the process to continue would lead to a crash when attempting to initialize IPC |
40 // channels that are not being set up in this test. | 48 // channels that are not being set up in this test. |
41 private static final String[] sProcessWaitArguments = { | 49 private static final String[] sProcessWaitArguments = { |
42 "_", "--" + BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER }; | 50 "_", "--" + BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER }; |
43 private static final String EXTERNAL_APK_PACKAGE_NAME = "org.chromium.extern al.apk"; | 51 private static final String EXTERNAL_APK_PACKAGE_NAME = "org.chromium.extern al.apk"; |
44 private static final String DEFAULT_SANDBOXED_PROCESS_SERVICE = | 52 private static final String DEFAULT_SANDBOXED_PROCESS_SERVICE = |
45 "org.chromium.content.app.SandboxedProcessService"; | 53 "org.chromium.content.app.SandboxedProcessService"; |
46 | 54 |
47 @Override | 55 @Rule |
48 protected void setUp() throws Exception { | 56 public CommandLineTestRule mCommandLineTestRule = new CommandLineTestRule(fa lse); |
49 super.setUp(); | |
50 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); | |
boliu
2017/03/23 18:04:08
why not use a @Before method?
the real yoland
2017/03/23 18:39:56
Because for testServiceFailedToBind, mCommandLineT
boliu
2017/03/23 18:42:32
Does that mean we no longer have per-test command
the real yoland
2017/03/23 19:01:03
yes
if we want per-test support, we need to use a
boliu
2017/03/23 19:02:51
If annotation still works, why do we have CommandL
the real yoland
2017/03/23 19:16:31
currently, annotation does not work. It would work
boliu
2017/03/23 20:08:56
Can we do that here then?
| |
51 } | |
52 | 57 |
53 /** | 58 /** |
54 * Tests cleanup for a connection that fails to connect in the first place. | 59 * Tests cleanup for a connection that fails to connect in the first place. |
55 */ | 60 */ |
61 @Test | |
56 @MediumTest | 62 @MediumTest |
57 @Feature({"ProcessManagement"}) | 63 @Feature({"ProcessManagement"}) |
58 @CommandLineFlags.Add(ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FOR _TESTING + "=4") | 64 public void testServiceFailedToBind() throws Exception { |
59 public void testServiceFailedToBind() { | 65 mCommandLineTestRule.setFlags( |
60 assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | 66 ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FOR_TESTING + "=4"); |
61 assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting()) ; | 67 mCommandLineTestRule.setUp(); |
68 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); | |
69 Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | |
70 Assert.assertEquals(0, ChildProcessLauncher.connectedServicesCountForTes ting()); | |
62 | 71 |
63 // Try to allocate a connection to service class in incorrect package. W e can do that by | 72 // Try to allocate a connection to service class in incorrect package. W e can do that by |
64 // using the instrumentation context (getContext()) instead of the app c ontext | 73 // using the instrumentation context (getContext()) instead of the app c ontext |
65 // (getTargetContext()). | 74 // (getTargetContext()). |
66 Context context = getInstrumentation().getContext(); | 75 Context context = InstrumentationRegistry.getInstrumentation().getContex t(); |
67 ChildProcessLauncher.allocateBoundConnectionForTesting( | 76 ChildProcessLauncher.allocateBoundConnectionForTesting( |
68 context, getDefaultChildProcessCreationParams(context.getPackage Name())); | 77 context, getDefaultChildProcessCreationParams(context.getPackage Name())); |
69 | 78 |
70 // Verify that the connection is not considered as allocated. | 79 // Verify that the connection is not considered as allocated. |
71 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 80 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
72 @Override | 81 @Override |
73 public Integer call() { | 82 public Integer call() { |
74 return allocatedChromeSandboxedConnectionsCount(); | 83 return allocatedChromeSandboxedConnectionsCount(); |
75 } | 84 } |
76 })); | 85 })); |
77 | 86 |
78 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 87 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
79 @Override | 88 @Override |
80 public Integer call() { | 89 public Integer call() { |
81 return ChildProcessLauncher.connectedServicesCountForTesting(); | 90 return ChildProcessLauncher.connectedServicesCountForTesting(); |
82 } | 91 } |
83 })); | 92 })); |
84 } | 93 } |
85 | 94 |
86 /** | 95 /** |
87 * Tests cleanup for a connection that terminates before setup. | 96 * Tests cleanup for a connection that terminates before setup. |
97 * @throws ProcessInitException | |
88 */ | 98 */ |
99 @Test | |
89 @MediumTest | 100 @MediumTest |
90 @Feature({"ProcessManagement"}) | 101 @Feature({"ProcessManagement"}) |
91 public void testServiceCrashedBeforeSetup() throws RemoteException { | 102 public void testServiceCrashedBeforeSetup() throws RemoteException, ProcessI nitException { |
92 assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | 103 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
93 assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting()) ; | 104 Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount()); |
105 Assert.assertEquals(0, ChildProcessLauncher.connectedServicesCountForTes ting()); | |
94 | 106 |
95 // Start and connect to a new service. | 107 // Start and connect to a new service. |
96 final ChildProcessConnectionImpl connection = startConnection(); | 108 final ChildProcessConnectionImpl connection = startConnection(); |
97 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 109 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); |
98 | 110 |
99 // Verify that the service is not yet set up. | 111 // Verify that the service is not yet set up. |
100 assertEquals(0, connection.getPid()); | 112 Assert.assertEquals(0, connection.getPid()); |
101 assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting()) ; | 113 Assert.assertEquals(0, ChildProcessLauncher.connectedServicesCountForTes ting()); |
102 | 114 |
103 // Crash the service. | 115 // Crash the service. |
104 assertTrue(connection.crashServiceForTesting()); | 116 Assert.assertTrue(connection.crashServiceForTesting()); |
105 | 117 |
106 // Verify that the connection gets cleaned-up. | 118 // Verify that the connection gets cleaned-up. |
107 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 119 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
108 @Override | 120 @Override |
109 public Integer call() { | 121 public Integer call() { |
110 return allocatedChromeSandboxedConnectionsCount(); | 122 return allocatedChromeSandboxedConnectionsCount(); |
111 } | 123 } |
112 })); | 124 })); |
113 | 125 |
114 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 126 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
115 @Override | 127 @Override |
116 public Integer call() { | 128 public Integer call() { |
117 return ChildProcessLauncher.connectedServicesCountForTesting(); | 129 return ChildProcessLauncher.connectedServicesCountForTesting(); |
118 } | 130 } |
119 })); | 131 })); |
120 } | 132 } |
121 | 133 |
122 /** | 134 /** |
123 * Tests cleanup for a connection that terminates after setup. | 135 * Tests cleanup for a connection that terminates after setup. |
136 * @throws ProcessInitException | |
124 */ | 137 */ |
138 @Test | |
125 @MediumTest | 139 @MediumTest |
126 @Feature({"ProcessManagement"}) | 140 @Feature({"ProcessManagement"}) |
127 public void testServiceCrashedAfterSetup() throws RemoteException { | 141 public void testServiceCrashedAfterSetup() throws RemoteException, ProcessIn itException { |
128 assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | 142 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
143 Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | |
129 | 144 |
130 // Start and connect to a new service. | 145 // Start and connect to a new service. |
131 final ChildProcessConnectionImpl connection = startConnection(); | 146 final ChildProcessConnectionImpl connection = startConnection(); |
132 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 147 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); |
133 | 148 |
134 // Initiate the connection setup. | 149 // Initiate the connection setup. |
135 triggerConnectionSetup(connection); | 150 triggerConnectionSetup(connection); |
136 | 151 |
137 // Verify that the connection completes the setup. | 152 // Verify that the connection completes the setup. |
138 CriteriaHelper.pollInstrumentationThread(Criteria.equals(1, new Callable <Integer>() { | 153 CriteriaHelper.pollInstrumentationThread(Criteria.equals(1, new Callable <Integer>() { |
139 @Override | 154 @Override |
140 public Integer call() { | 155 public Integer call() { |
141 return ChildProcessLauncher.connectedServicesCountForTesting(); | 156 return ChildProcessLauncher.connectedServicesCountForTesting(); |
142 } | 157 } |
143 })); | 158 })); |
144 | 159 |
145 CriteriaHelper.pollInstrumentationThread( | 160 CriteriaHelper.pollInstrumentationThread( |
146 new Criteria("The connection failed to get a pid in setup.") { | 161 new Criteria("The connection failed to get a pid in setup.") { |
147 @Override | 162 @Override |
148 public boolean isSatisfied() { | 163 public boolean isSatisfied() { |
149 return connection.getPid() != 0; | 164 return connection.getPid() != 0; |
150 } | 165 } |
151 }); | 166 }); |
152 | 167 |
153 // Crash the service. | 168 // Crash the service. |
154 assertTrue(connection.crashServiceForTesting()); | 169 Assert.assertTrue(connection.crashServiceForTesting()); |
155 | 170 |
156 // Verify that the connection gets cleaned-up. | 171 // Verify that the connection gets cleaned-up. |
157 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 172 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
158 @Override | 173 @Override |
159 public Integer call() { | 174 public Integer call() { |
160 return allocatedChromeSandboxedConnectionsCount(); | 175 return allocatedChromeSandboxedConnectionsCount(); |
161 } | 176 } |
162 })); | 177 })); |
163 | 178 |
164 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 179 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
165 @Override | 180 @Override |
166 public Integer call() { | 181 public Integer call() { |
167 return ChildProcessLauncher.connectedServicesCountForTesting(); | 182 return ChildProcessLauncher.connectedServicesCountForTesting(); |
168 } | 183 } |
169 })); | 184 })); |
170 | 185 |
171 // Verify that the connection pid remains set after termination. | 186 // Verify that the connection pid remains set after termination. |
172 assertTrue(connection.getPid() != 0); | 187 Assert.assertTrue(connection.getPid() != 0); |
173 } | 188 } |
174 | 189 |
175 /** | 190 /** |
176 * Tests spawning a pending process from queue. | 191 * Tests spawning a pending process from queue. |
192 * @throws ProcessInitException | |
177 */ | 193 */ |
194 @Test | |
178 @MediumTest | 195 @MediumTest |
179 @Feature({"ProcessManagement"}) | 196 @Feature({"ProcessManagement"}) |
180 public void testPendingSpawnQueue() throws RemoteException { | 197 public void testPendingSpawnQueue() throws RemoteException, ProcessInitExcep tion { |
181 final Context appContext = getInstrumentation().getTargetContext(); | 198 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
182 assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | 199 final Context appContext = InstrumentationRegistry.getInstrumentation(). getTargetContext(); |
200 Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | |
183 | 201 |
184 // Start and connect to a new service. | 202 // Start and connect to a new service. |
185 final ChildProcessConnectionImpl connection = startConnection(); | 203 final ChildProcessConnectionImpl connection = startConnection(); |
186 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 204 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); |
187 | 205 |
188 // Queue up a new spawn request. There is no way to kill the pending con nection, leak it | 206 // Queue up a new spawn request. There is no way to kill the pending con nection, leak it |
189 // until the browser restart. | 207 // until the browser restart. |
190 final String packageName = appContext.getPackageName(); | 208 final String packageName = appContext.getPackageName(); |
191 final boolean inSandbox = true; | 209 final boolean inSandbox = true; |
192 ChildProcessLauncher.enqueuePendingSpawnForTesting(appContext, sProcessW aitArguments, | 210 ChildProcessLauncher.enqueuePendingSpawnForTesting(appContext, sProcessW aitArguments, |
193 getDefaultChildProcessCreationParams(packageName), inSandbox); | 211 getDefaultChildProcessCreationParams(packageName), inSandbox); |
194 assertEquals(1, ChildProcessLauncher.pendingSpawnsCountForTesting(appCon text, packageName, | 212 Assert.assertEquals(1, |
195 inSandbox)); | 213 ChildProcessLauncher.pendingSpawnsCountForTesting( |
214 appContext, packageName, inSandbox)); | |
196 | 215 |
197 // Initiate the connection setup. | 216 // Initiate the connection setup. |
198 triggerConnectionSetup(connection); | 217 triggerConnectionSetup(connection); |
199 | 218 |
200 // Verify that the connection completes the setup. | 219 // Verify that the connection completes the setup. |
201 CriteriaHelper.pollInstrumentationThread( | 220 CriteriaHelper.pollInstrumentationThread( |
202 Criteria.equals(1, new Callable<Integer>() { | 221 Criteria.equals(1, new Callable<Integer>() { |
203 @Override | 222 @Override |
204 public Integer call() { | 223 public Integer call() { |
205 return ChildProcessLauncher.connectedServicesCountForTes ting(); | 224 return ChildProcessLauncher.connectedServicesCountForTes ting(); |
206 } | 225 } |
207 })); | 226 })); |
208 | 227 |
209 CriteriaHelper.pollInstrumentationThread( | 228 CriteriaHelper.pollInstrumentationThread( |
210 new Criteria("The connection failed to get a pid in setup.") { | 229 new Criteria("The connection failed to get a pid in setup.") { |
211 @Override | 230 @Override |
212 public boolean isSatisfied() { | 231 public boolean isSatisfied() { |
213 return connection.getPid() != 0; | 232 return connection.getPid() != 0; |
214 } | 233 } |
215 }); | 234 }); |
216 | 235 |
217 // Crash the service. | 236 // Crash the service. |
218 assertTrue(connection.crashServiceForTesting()); | 237 Assert.assertTrue(connection.crashServiceForTesting()); |
219 | 238 |
220 // Verify that a new service is started for the pending spawn. | 239 // Verify that a new service is started for the pending spawn. |
221 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { | 240 CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable <Integer>() { |
222 @Override | 241 @Override |
223 public Integer call() { | 242 public Integer call() { |
224 return ChildProcessLauncher.pendingSpawnsCountForTesting(appCont ext, packageName, | 243 return ChildProcessLauncher.pendingSpawnsCountForTesting(appCont ext, packageName, |
225 inSandbox); | 244 inSandbox); |
226 } | 245 } |
227 })); | 246 })); |
228 | 247 |
(...skipping 10 matching lines...) Expand all Loading... | |
239 @Override | 258 @Override |
240 public Integer call() { | 259 public Integer call() { |
241 return ChildProcessLauncher.connectedServicesCountForTesting(); | 260 return ChildProcessLauncher.connectedServicesCountForTesting(); |
242 } | 261 } |
243 })); | 262 })); |
244 } | 263 } |
245 | 264 |
246 /** | 265 /** |
247 * Tests service number of connections for external APKs and regular tabs ar e assigned properly, | 266 * Tests service number of connections for external APKs and regular tabs ar e assigned properly, |
248 * i.e. from different ChildConnectionAllocators. | 267 * i.e. from different ChildConnectionAllocators. |
268 * @throws ProcessInitException | |
249 */ | 269 */ |
270 @Test | |
250 @MediumTest | 271 @MediumTest |
251 @Feature({"ProcessManagement"}) | 272 @Feature({"ProcessManagement"}) |
252 @CommandLineFlags.Add({ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FO R_TESTING + "=4", | 273 public void testServiceNumberAllocation() throws ProcessInitException { |
253 ChildProcessLauncher.SWITCH_SANDBOXED_SERVICES_NAME_FOR_TESTING + "= " | 274 mCommandLineTestRule.setFlags( |
254 + DEFAULT_SANDBOXED_PROCESS_SERVICE}) | 275 ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FOR_TESTING + "=4", |
255 public void testServiceNumberAllocation() { | 276 ChildProcessLauncher.SWITCH_SANDBOXED_SERVICES_NAME_FOR_TESTING + "=" |
256 Context appContext = getInstrumentation().getTargetContext(); | 277 + DEFAULT_SANDBOXED_PROCESS_SERVICE); |
257 assertEquals(0, ChildProcessLauncher.allocatedSandboxedConnectionsCountF orTesting( | 278 |
258 appContext, EXTERNAL_APK_PACKAGE_NAME)); | 279 mCommandLineTestRule.setUp(); |
259 assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | 280 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
281 Context appContext = InstrumentationRegistry.getInstrumentation().getTar getContext(); | |
282 Assert.assertEquals(0, | |
283 ChildProcessLauncher.allocatedSandboxedConnectionsCountForTestin g( | |
284 appContext, EXTERNAL_APK_PACKAGE_NAME)); | |
285 Assert.assertEquals(0, allocatedChromeSandboxedConnectionsCount()); | |
260 | 286 |
261 // Start and connect to a new service of an external APK. | 287 // Start and connect to a new service of an external APK. |
262 ChildProcessConnectionImpl externalApkConnection = | 288 ChildProcessConnectionImpl externalApkConnection = |
263 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); | 289 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); |
264 // Start and connect to a new service for a regular tab. | 290 // Start and connect to a new service for a regular tab. |
265 ChildProcessConnectionImpl tabConnection = allocateConnection(appContext .getPackageName()); | 291 ChildProcessConnectionImpl tabConnection = allocateConnection(appContext .getPackageName()); |
266 | 292 |
267 // Verify that one connection is allocated for an external APK and a reg ular tab | 293 // Verify that one connection is allocated for an external APK and a reg ular tab |
268 // respectively. | 294 // respectively. |
269 assertEquals(1, ChildProcessLauncher.allocatedSandboxedConnectionsCountF orTesting( | 295 Assert.assertEquals(1, |
270 appContext, EXTERNAL_APK_PACKAGE_NAME)); | 296 ChildProcessLauncher.allocatedSandboxedConnectionsCountForTestin g( |
271 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 297 appContext, EXTERNAL_APK_PACKAGE_NAME)); |
298 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | |
272 | 299 |
273 // Verify that connections allocated for an external APK and the regular tab are from | 300 // Verify that connections allocated for an external APK and the regular tab are from |
274 // different ChildConnectionAllocators, since both ChildConnectionAlloca tors start | 301 // different ChildConnectionAllocators, since both ChildConnectionAlloca tors start |
275 // allocating connections from number 0. | 302 // allocating connections from number 0. |
276 assertEquals(0, externalApkConnection.getServiceNumber()); | 303 Assert.assertEquals(0, externalApkConnection.getServiceNumber()); |
277 assertEquals(0, tabConnection.getServiceNumber()); | 304 Assert.assertEquals(0, tabConnection.getServiceNumber()); |
278 } | 305 } |
279 | 306 |
280 /** | 307 /** |
281 * Tests that after reaching the maximum allowed connections for an external APK, we can't | 308 * Tests that after reaching the maximum allowed connections for an external APK, we can't |
282 * allocate a new connection to the APK, but we can still allocate a connect ion for a regular | 309 * allocate a new connection to the APK, but we can still allocate a connect ion for a regular |
283 * tab. | 310 * tab. |
311 * @throws ProcessInitException | |
284 */ | 312 */ |
313 @Test | |
285 @MediumTest | 314 @MediumTest |
286 @Feature({"ProcessManagement"}) | 315 @Feature({"ProcessManagement"}) |
287 @CommandLineFlags.Add({ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FO R_TESTING + "=1", | 316 public void testExceedMaximumConnectionNumber() throws ProcessInitException { |
288 ChildProcessLauncher.SWITCH_SANDBOXED_SERVICES_NAME_FOR_TESTING + "= " | 317 mCommandLineTestRule.setFlags( |
289 + DEFAULT_SANDBOXED_PROCESS_SERVICE}) | 318 ChildProcessLauncher.SWITCH_NUM_SANDBOXED_SERVICES_FOR_TESTING + "=1", |
290 public void testExceedMaximumConnectionNumber() { | 319 ChildProcessLauncher.SWITCH_SANDBOXED_SERVICES_NAME_FOR_TESTING + "=" |
291 Context appContext = getInstrumentation().getTargetContext(); | 320 + DEFAULT_SANDBOXED_PROCESS_SERVICE); |
292 assertEquals(0, ChildProcessLauncher.allocatedSandboxedConnectionsCountF orTesting( | 321 mCommandLineTestRule.setUp(); |
293 appContext, EXTERNAL_APK_PACKAGE_NAME)); | 322 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
323 Context appContext = InstrumentationRegistry.getInstrumentation().getTar getContext(); | |
324 Assert.assertEquals(0, | |
325 ChildProcessLauncher.allocatedSandboxedConnectionsCountForTestin g( | |
326 appContext, EXTERNAL_APK_PACKAGE_NAME)); | |
294 | 327 |
295 // Setup a connection for an external APK to reach the maximum allowed c onnection number. | 328 // Setup a connection for an external APK to reach the maximum allowed c onnection number. |
296 ChildProcessConnectionImpl externalApkConnection = | 329 ChildProcessConnectionImpl externalApkConnection = |
297 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); | 330 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); |
298 assertNotNull(externalApkConnection); | 331 Assert.assertNotNull(externalApkConnection); |
299 | 332 |
300 // Verify that there isn't any connection available for the external APK . | 333 // Verify that there isn't any connection available for the external APK . |
301 ChildProcessConnectionImpl exceedNumberExternalApkConnection = | 334 ChildProcessConnectionImpl exceedNumberExternalApkConnection = |
302 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); | 335 allocateConnection(EXTERNAL_APK_PACKAGE_NAME); |
303 assertNull(exceedNumberExternalApkConnection); | 336 Assert.assertNull(exceedNumberExternalApkConnection); |
304 | 337 |
305 // Verify that we can still allocate connection for a regular tab. | 338 // Verify that we can still allocate connection for a regular tab. |
306 ChildProcessConnectionImpl tabConnection = allocateConnection(appContext .getPackageName()); | 339 ChildProcessConnectionImpl tabConnection = allocateConnection(appContext .getPackageName()); |
307 assertNotNull(tabConnection); | 340 Assert.assertNotNull(tabConnection); |
308 } | 341 } |
309 | 342 |
310 /** | 343 /** |
311 * Tests binding to the same sandboxed service process from multiple process es in the | 344 * Tests binding to the same sandboxed service process from multiple process es in the |
312 * same package. This uses the ChildProcessLauncherTestHelperService declare d in | 345 * same package. This uses the ChildProcessLauncherTestHelperService declare d in |
313 * ContentShell.apk as a separate android:process to bind the first (slot 0) service. The | 346 * ContentShell.apk as a separate android:process to bind the first (slot 0) service. The |
314 * instrumentation test then tries to bind the same slot, which fails, so th e | 347 * instrumentation test then tries to bind the same slot, which fails, so th e |
315 * ChildProcessLauncher retries on a new connection. | 348 * ChildProcessLauncher retries on a new connection. |
349 * @throws ProcessInitException | |
316 */ | 350 */ |
351 @Test | |
317 @MediumTest | 352 @MediumTest |
318 @Feature({"ProcessManagement"}) | 353 @Feature({"ProcessManagement"}) |
319 public void testBindServiceFromMultipleProcesses() throws RemoteException { | 354 public void testBindServiceFromMultipleProcesses() |
320 final Context context = getInstrumentation().getTargetContext(); | 355 throws RemoteException, ProcessInitException { |
356 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); | |
357 final Context context = InstrumentationRegistry.getInstrumentation().get TargetContext(); | |
321 | 358 |
322 // Start the Helper service. | 359 // Start the Helper service. |
323 class HelperConnection implements ServiceConnection { | 360 class HelperConnection implements ServiceConnection { |
324 Messenger mMessenger = null; | 361 Messenger mMessenger = null; |
325 | 362 |
326 @Override | 363 @Override |
327 public void onServiceConnected(ComponentName name, IBinder service) { | 364 public void onServiceConnected(ComponentName name, IBinder service) { |
328 mMessenger = new Messenger(service); | 365 mMessenger = new Messenger(service); |
329 } | 366 } |
330 | 367 |
331 @Override | 368 @Override |
332 public void onServiceDisconnected(ComponentName name) {} | 369 public void onServiceDisconnected(ComponentName name) {} |
333 } | 370 } |
334 final HelperConnection serviceConn = new HelperConnection(); | 371 final HelperConnection serviceConn = new HelperConnection(); |
335 | 372 |
336 Intent intent = new Intent(); | 373 Intent intent = new Intent(); |
337 intent.setComponent(new ComponentName(context.getPackageName(), | 374 intent.setComponent(new ComponentName(context.getPackageName(), |
338 context.getPackageName() + ".ChildProcessLauncherTestHelperServi ce")); | 375 context.getPackageName() + ".ChildProcessLauncherTestHelperServi ce")); |
339 assertTrue(context.bindService(intent, serviceConn, Context.BIND_AUTO_CR EATE)); | 376 Assert.assertTrue(context.bindService(intent, serviceConn, Context.BIND_ AUTO_CREATE)); |
340 | 377 |
341 // Wait for the Helper service to connect. | 378 // Wait for the Helper service to connect. |
342 CriteriaHelper.pollInstrumentationThread( | 379 CriteriaHelper.pollInstrumentationThread( |
343 new Criteria("Failed to get helper service Messenger") { | 380 new Criteria("Failed to get helper service Messenger") { |
344 @Override | 381 @Override |
345 public boolean isSatisfied() { | 382 public boolean isSatisfied() { |
346 return serviceConn.mMessenger != null; | 383 return serviceConn.mMessenger != null; |
347 } | 384 } |
348 }); | 385 }); |
349 | 386 |
350 assertNotNull(serviceConn.mMessenger); | 387 Assert.assertNotNull(serviceConn.mMessenger); |
351 | 388 |
352 class ReplyHandler implements Handler.Callback { | 389 class ReplyHandler implements Handler.Callback { |
353 Message mMessage; | 390 Message mMessage; |
354 | 391 |
355 @Override | 392 @Override |
356 public boolean handleMessage(Message msg) { | 393 public boolean handleMessage(Message msg) { |
357 // Copy the message so its contents outlive this Binder transact ion. | 394 // Copy the message so its contents outlive this Binder transact ion. |
358 mMessage = Message.obtain(); | 395 mMessage = Message.obtain(); |
359 mMessage.copyFrom(msg); | 396 mMessage.copyFrom(msg); |
360 return true; | 397 return true; |
(...skipping 10 matching lines...) Expand all Loading... | |
371 | 408 |
372 CriteriaHelper.pollInstrumentationThread( | 409 CriteriaHelper.pollInstrumentationThread( |
373 new Criteria("Failed waiting for helper service reply") { | 410 new Criteria("Failed waiting for helper service reply") { |
374 @Override | 411 @Override |
375 public boolean isSatisfied() { | 412 public boolean isSatisfied() { |
376 return replyHandler.mMessage != null; | 413 return replyHandler.mMessage != null; |
377 } | 414 } |
378 }); | 415 }); |
379 | 416 |
380 // Verify that the Helper was able to launch the sandboxed service. | 417 // Verify that the Helper was able to launch the sandboxed service. |
381 assertNotNull(replyHandler.mMessage); | 418 Assert.assertNotNull(replyHandler.mMessage); |
382 assertEquals(ChildProcessLauncherTestHelperService.MSG_BIND_SERVICE_REPL Y, | 419 Assert.assertEquals(ChildProcessLauncherTestHelperService.MSG_BIND_SERVI CE_REPLY, |
383 replyHandler.mMessage.what); | 420 replyHandler.mMessage.what); |
384 assertEquals("Connection slot from helper service is not 0", 0, replyHan dler.mMessage.arg2); | 421 Assert.assertEquals( |
422 "Connection slot from helper service is not 0", 0, replyHandler. mMessage.arg2); | |
385 | 423 |
386 final int helperConnPid = replyHandler.mMessage.arg1; | 424 final int helperConnPid = replyHandler.mMessage.arg1; |
387 assertTrue(helperConnPid > 0); | 425 Assert.assertTrue(helperConnPid > 0); |
388 | 426 |
389 // Launch a service from this process. Since slot 0 is already bound by the Helper, it | 427 // Launch a service from this process. Since slot 0 is already bound by the Helper, it |
390 // will fail to start and the ChildProcessLauncher will retry. | 428 // will fail to start and the ChildProcessLauncher will retry. |
391 final ChildProcessConnection conn = ChildProcessLauncher.startForTesting (context, | 429 final ChildProcessConnection conn = ChildProcessLauncher.startForTesting (context, |
392 sProcessWaitArguments, new FileDescriptorInfo[0], | 430 sProcessWaitArguments, new FileDescriptorInfo[0], |
393 getDefaultChildProcessCreationParams(context.getPackageName())); | 431 getDefaultChildProcessCreationParams(context.getPackageName())); |
394 | 432 |
395 CriteriaHelper.pollInstrumentationThread( | 433 CriteriaHelper.pollInstrumentationThread( |
396 new Criteria("Failed waiting for instrumentation-bound service") { | 434 new Criteria("Failed waiting for instrumentation-bound service") { |
397 @Override | 435 @Override |
398 public boolean isSatisfied() { | 436 public boolean isSatisfied() { |
399 return conn.getService() != null; | 437 return conn.getService() != null; |
400 } | 438 } |
401 }); | 439 }); |
402 | 440 |
403 assertEquals(0, conn.getServiceNumber()); | 441 Assert.assertEquals(0, conn.getServiceNumber()); |
404 | 442 |
405 final ChildProcessConnection[] sandboxedConnections = | 443 final ChildProcessConnection[] sandboxedConnections = |
406 ChildProcessLauncher.getSandboxedConnectionArrayForTesting( | 444 ChildProcessLauncher.getSandboxedConnectionArrayForTesting( |
407 context.getPackageName()); | 445 context.getPackageName()); |
408 | 446 |
409 // Wait for the retry to succeed. | 447 // Wait for the retry to succeed. |
410 CriteriaHelper.pollInstrumentationThread( | 448 CriteriaHelper.pollInstrumentationThread( |
411 new Criteria("Failed waiting for both child process services") { | 449 new Criteria("Failed waiting for both child process services") { |
412 @Override | 450 @Override |
413 public boolean isSatisfied() { | 451 public boolean isSatisfied() { |
414 boolean allChildrenConnected = true; | 452 boolean allChildrenConnected = true; |
415 for (int i = 0; i <= 1; ++i) { | 453 for (int i = 0; i <= 1; ++i) { |
416 ChildProcessConnection conn = sandboxedConnections[i ]; | 454 ChildProcessConnection conn = sandboxedConnections[i ]; |
417 allChildrenConnected &= conn != null && conn.getServ ice() != null; | 455 allChildrenConnected &= conn != null && conn.getServ ice() != null; |
418 } | 456 } |
419 return allChildrenConnected; | 457 return allChildrenConnected; |
420 } | 458 } |
421 }); | 459 }); |
422 | 460 |
423 // Check that only two connections are created. | 461 // Check that only two connections are created. |
424 for (int i = 0; i < sandboxedConnections.length; ++i) { | 462 for (int i = 0; i < sandboxedConnections.length; ++i) { |
425 ChildProcessConnection sandboxedConn = sandboxedConnections[i]; | 463 ChildProcessConnection sandboxedConn = sandboxedConnections[i]; |
426 if (i <= 1) { | 464 if (i <= 1) { |
427 assertNotNull(sandboxedConn); | 465 Assert.assertNotNull(sandboxedConn); |
428 assertNotNull(sandboxedConn.getService()); | 466 Assert.assertNotNull(sandboxedConn.getService()); |
429 } else { | 467 } else { |
430 assertNull(sandboxedConn); | 468 Assert.assertNull(sandboxedConn); |
431 } | 469 } |
432 } | 470 } |
433 | 471 |
434 assertTrue(conn == sandboxedConnections[0]); | 472 Assert.assertTrue(conn == sandboxedConnections[0]); |
435 final ChildProcessConnection retryConn = sandboxedConnections[1]; | 473 final ChildProcessConnection retryConn = sandboxedConnections[1]; |
436 | 474 |
437 assertFalse(conn == retryConn); | 475 Assert.assertFalse(conn == retryConn); |
438 | 476 |
439 assertEquals(0, conn.getServiceNumber()); | 477 Assert.assertEquals(0, conn.getServiceNumber()); |
440 assertEquals(0, conn.getPid()); | 478 Assert.assertEquals(0, conn.getPid()); |
441 assertFalse(conn.getService().bindToCaller()); | 479 Assert.assertFalse(conn.getService().bindToCaller()); |
442 | 480 |
443 assertEquals(1, retryConn.getServiceNumber()); | 481 Assert.assertEquals(1, retryConn.getServiceNumber()); |
444 assertTrue(retryConn.getPid() > 0); | 482 Assert.assertTrue(retryConn.getPid() > 0); |
445 assertTrue(retryConn.getPid() != helperConnPid); | 483 Assert.assertTrue(retryConn.getPid() != helperConnPid); |
446 assertTrue(retryConn.getService().bindToCaller()); | 484 Assert.assertTrue(retryConn.getService().bindToCaller()); |
447 } | 485 } |
448 | 486 |
487 @Test | |
449 @MediumTest | 488 @MediumTest |
450 @Feature({"ProcessManagement"}) | 489 @Feature({"ProcessManagement"}) |
451 public void testWarmUp() { | 490 public void testWarmUp() throws ProcessInitException { |
452 Context context = getInstrumentation().getTargetContext(); | 491 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); |
492 Context context = InstrumentationRegistry.getInstrumentation().getTarget Context(); | |
453 ChildProcessLauncher.warmUp(context); // Not on UI thread. | 493 ChildProcessLauncher.warmUp(context); // Not on UI thread. |
454 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 494 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); |
455 | 495 |
456 final ChildProcessConnection conn = ChildProcessLauncher.startForTesting ( | 496 final ChildProcessConnection conn = ChildProcessLauncher.startForTesting ( |
457 context, new String[0], new FileDescriptorInfo[0], null); | 497 context, new String[0], new FileDescriptorInfo[0], null); |
458 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); // Used war mup connection. | 498 Assert.assertEquals( |
499 1, allocatedChromeSandboxedConnectionsCount()); // Used warmup c onnection. | |
459 | 500 |
460 ChildProcessLauncher.stop(conn.getPid()); | 501 ChildProcessLauncher.stop(conn.getPid()); |
461 } | 502 } |
462 | 503 |
504 @Test | |
463 @MediumTest | 505 @MediumTest |
464 @Feature({"ProcessManagement"}) | 506 @Feature({"ProcessManagement"}) |
465 public void testCustomCreationParamDoesNotReuseWarmupConnection() { | 507 public void testCustomCreationParamDoesNotReuseWarmupConnection() throws Pro cessInitException { |
508 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).ensureInitialized(); | |
466 // Since warmUp only uses default params. | 509 // Since warmUp only uses default params. |
467 Context context = getInstrumentation().getTargetContext(); | 510 Context context = InstrumentationRegistry.getInstrumentation().getTarget Context(); |
468 // Check uses object identity, having the params match exactly is fine. | 511 // Check uses object identity, having the params match exactly is fine. |
469 ChildProcessCreationParams.registerDefault( | 512 ChildProcessCreationParams.registerDefault( |
470 getDefaultChildProcessCreationParams(context.getPackageName())); | 513 getDefaultChildProcessCreationParams(context.getPackageName())); |
471 int paramId = ChildProcessCreationParams.register( | 514 int paramId = ChildProcessCreationParams.register( |
472 getDefaultChildProcessCreationParams(context.getPackageName())); | 515 getDefaultChildProcessCreationParams(context.getPackageName())); |
473 | 516 |
474 ChildProcessLauncher.warmUp(context); // Not on UI thread. | 517 ChildProcessLauncher.warmUp(context); // Not on UI thread. |
475 assertEquals(1, allocatedChromeSandboxedConnectionsCount()); | 518 Assert.assertEquals(1, allocatedChromeSandboxedConnectionsCount()); |
476 | 519 |
477 startRendererProcess(context, paramId, new FileDescriptorInfo[0]); | 520 startRendererProcess(context, paramId, new FileDescriptorInfo[0]); |
478 assertEquals(2, allocatedChromeSandboxedConnectionsCount()); // Warmup n ot used. | 521 Assert.assertEquals(2, allocatedChromeSandboxedConnectionsCount()); // W armup not used. |
479 | 522 |
480 startRendererProcess( | 523 startRendererProcess( |
481 context, ChildProcessCreationParams.DEFAULT_ID, new FileDescript orInfo[0]); | 524 context, ChildProcessCreationParams.DEFAULT_ID, new FileDescript orInfo[0]); |
482 assertEquals(2, allocatedChromeSandboxedConnectionsCount()); // Warmup u sed. | 525 Assert.assertEquals(2, allocatedChromeSandboxedConnectionsCount()); // W armup used. |
483 | 526 |
484 ChildProcessCreationParams.unregister(paramId); | 527 ChildProcessCreationParams.unregister(paramId); |
485 } | 528 } |
486 | 529 |
487 private ChildProcessConnectionImpl startConnection() { | 530 private ChildProcessConnectionImpl startConnection() { |
488 // Allocate a new connection. | 531 // Allocate a new connection. |
489 Context context = getInstrumentation().getTargetContext(); | 532 Context context = InstrumentationRegistry.getInstrumentation().getTarget Context(); |
490 final ChildProcessConnectionImpl connection = | 533 final ChildProcessConnectionImpl connection = |
491 (ChildProcessConnectionImpl) ChildProcessLauncher.allocateBoundC onnectionForTesting( | 534 (ChildProcessConnectionImpl) ChildProcessLauncher.allocateBoundC onnectionForTesting( |
492 context, getDefaultChildProcessCreationParams(context.ge tPackageName())); | 535 context, getDefaultChildProcessCreationParams(context.ge tPackageName())); |
493 | 536 |
494 // Wait for the service to connect. | 537 // Wait for the service to connect. |
495 CriteriaHelper.pollInstrumentationThread( | 538 CriteriaHelper.pollInstrumentationThread( |
496 new Criteria("The connection wasn't established.") { | 539 new Criteria("The connection wasn't established.") { |
497 @Override | 540 @Override |
498 public boolean isSatisfied() { | 541 public boolean isSatisfied() { |
499 return connection.isConnected(); | 542 return connection.isConnected(); |
(...skipping 10 matching lines...) Expand all Loading... | |
510 0 /* childProcessId */, filesToMap, 0 /* clientContext */); | 553 0 /* childProcessId */, filesToMap, 0 /* clientContext */); |
511 } | 554 } |
512 | 555 |
513 /** | 556 /** |
514 * Returns a new connection if it is allocated. Note this function only allo cates a connection | 557 * Returns a new connection if it is allocated. Note this function only allo cates a connection |
515 * but doesn't really start the connection to bind a service. It is for test ing whether the | 558 * but doesn't really start the connection to bind a service. It is for test ing whether the |
516 * connection is allocated properly for different application packages. | 559 * connection is allocated properly for different application packages. |
517 */ | 560 */ |
518 private ChildProcessConnectionImpl allocateConnection(String packageName) { | 561 private ChildProcessConnectionImpl allocateConnection(String packageName) { |
519 // Allocate a new connection. | 562 // Allocate a new connection. |
520 Context context = getInstrumentation().getTargetContext(); | 563 Context context = InstrumentationRegistry.getInstrumentation().getTarget Context(); |
521 return (ChildProcessConnectionImpl) ChildProcessLauncher.allocateConnect ionForTesting( | 564 return (ChildProcessConnectionImpl) ChildProcessLauncher.allocateConnect ionForTesting( |
522 context, getDefaultChildProcessCreationParams(packageNam e)); | 565 context, getDefaultChildProcessCreationParams(packageNam e)); |
523 } | 566 } |
524 | 567 |
525 /** | 568 /** |
526 * Returns the number of Chrome's sandboxed connections. | 569 * Returns the number of Chrome's sandboxed connections. |
527 */ | 570 */ |
528 private int allocatedChromeSandboxedConnectionsCount() { | 571 private int allocatedChromeSandboxedConnectionsCount() { |
529 Context context = getInstrumentation().getTargetContext(); | 572 Context context = InstrumentationRegistry.getInstrumentation().getTarget Context(); |
530 return ChildProcessLauncher.allocatedSandboxedConnectionsCountForTesting ( | 573 return ChildProcessLauncher.allocatedSandboxedConnectionsCountForTesting ( |
531 context, context.getPackageName()); | 574 context, context.getPackageName()); |
532 } | 575 } |
533 | 576 |
534 private ChildProcessCreationParams getDefaultChildProcessCreationParams(Stri ng packageName) { | 577 private ChildProcessCreationParams getDefaultChildProcessCreationParams(Stri ng packageName) { |
535 return new ChildProcessCreationParams(packageName, false /* isExternalSe rvice */, | 578 return new ChildProcessCreationParams(packageName, false /* isExternalSe rvice */, |
536 LibraryProcessType.PROCESS_CHILD); | 579 LibraryProcessType.PROCESS_CHILD); |
537 } | 580 } |
538 | 581 |
539 private void triggerConnectionSetup(ChildProcessConnectionImpl connection) { | 582 private void triggerConnectionSetup(ChildProcessConnectionImpl connection) { |
540 ChildProcessLauncher.triggerConnectionSetup(connection, sProcessWaitArgu ments, 1, | 583 ChildProcessLauncher.triggerConnectionSetup(connection, sProcessWaitArgu ments, 1, |
541 new FileDescriptorInfo[0], ChildProcessLauncher.CALLBACK_FOR_REN DERER_PROCESS, 0); | 584 new FileDescriptorInfo[0], ChildProcessLauncher.CALLBACK_FOR_REN DERER_PROCESS, 0); |
542 } | 585 } |
543 } | 586 } |
OLD | NEW |