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.annotation.SuppressLint; | 7 import android.annotation.SuppressLint; |
8 import android.app.Service; | |
8 import android.content.Context; | 9 import android.content.Context; |
9 import android.content.Intent; | 10 import android.content.Intent; |
10 import android.content.pm.ApplicationInfo; | 11 import android.content.pm.ApplicationInfo; |
11 import android.content.pm.PackageManager; | 12 import android.content.pm.PackageManager; |
12 import android.graphics.SurfaceTexture; | 13 import android.graphics.SurfaceTexture; |
13 import android.os.Build; | 14 import android.os.Build; |
14 import android.os.Bundle; | 15 import android.os.Bundle; |
15 import android.os.ParcelFileDescriptor; | 16 import android.os.ParcelFileDescriptor; |
16 import android.os.RemoteException; | 17 import android.os.RemoteException; |
17 import android.text.TextUtils; | 18 import android.text.TextUtils; |
18 import android.util.Pair; | 19 import android.util.Pair; |
19 import android.view.Surface; | 20 import android.view.Surface; |
20 | 21 |
21 import org.chromium.base.CommandLine; | 22 import org.chromium.base.CommandLine; |
22 import org.chromium.base.CpuFeatures; | 23 import org.chromium.base.CpuFeatures; |
23 import org.chromium.base.Log; | 24 import org.chromium.base.Log; |
24 import org.chromium.base.ThreadUtils; | 25 import org.chromium.base.ThreadUtils; |
25 import org.chromium.base.TraceEvent; | 26 import org.chromium.base.TraceEvent; |
26 import org.chromium.base.VisibleForTesting; | 27 import org.chromium.base.VisibleForTesting; |
27 import org.chromium.base.annotations.CalledByNative; | 28 import org.chromium.base.annotations.CalledByNative; |
28 import org.chromium.base.annotations.JNINamespace; | 29 import org.chromium.base.annotations.JNINamespace; |
29 import org.chromium.base.library_loader.Linker; | 30 import org.chromium.base.library_loader.Linker; |
30 import org.chromium.content.app.ChildProcessService; | |
31 import org.chromium.content.app.ChromiumLinkerParams; | 31 import org.chromium.content.app.ChromiumLinkerParams; |
32 import org.chromium.content.app.DownloadProcessService; | 32 import org.chromium.content.app.DownloadProcessService; |
33 import org.chromium.content.app.PrivilegedProcessService; | 33 import org.chromium.content.app.PrivilegedProcessService; |
34 import org.chromium.content.app.SandboxedProcessService; | 34 import org.chromium.content.app.SandboxedProcessService; |
35 import org.chromium.content.common.ContentSwitches; | 35 import org.chromium.content.common.ContentSwitches; |
36 import org.chromium.content.common.IChildProcessCallback; | 36 import org.chromium.content.common.IChildProcessCallback; |
37 import org.chromium.content.common.SurfaceWrapper; | 37 import org.chromium.content.common.SurfaceWrapper; |
38 | 38 |
39 import java.io.IOException; | 39 import java.io.IOException; |
40 import java.util.ArrayList; | 40 import java.util.ArrayList; |
(...skipping 17 matching lines...) Expand all Loading... | |
58 | 58 |
59 private static class ChildConnectionAllocator { | 59 private static class ChildConnectionAllocator { |
60 // Connections to services. Indices of the array correspond to the servi ce numbers. | 60 // Connections to services. Indices of the array correspond to the servi ce numbers. |
61 private final ChildProcessConnection[] mChildProcessConnections; | 61 private final ChildProcessConnection[] mChildProcessConnections; |
62 | 62 |
63 // The list of free (not bound) service indices. | 63 // The list of free (not bound) service indices. |
64 // SHOULD BE ACCESSED WITH mConnectionLock. | 64 // SHOULD BE ACCESSED WITH mConnectionLock. |
65 private final ArrayList<Integer> mFreeConnectionIndices; | 65 private final ArrayList<Integer> mFreeConnectionIndices; |
66 private final Object mConnectionLock = new Object(); | 66 private final Object mConnectionLock = new Object(); |
67 | 67 |
68 private Class<? extends ChildProcessService> mChildClass; | 68 private Class<? extends Service> mChildClass; |
69 private final boolean mInSandbox; | 69 private final boolean mInSandbox; |
70 // Each Allocator keeps a queue for the pending spawn data. Once a conne ction is free, we | 70 // Each Allocator keeps a queue for the pending spawn data. Once a conne ction is free, we |
71 // dequeue the pending spawn data from the same allocator as the connect ion. | 71 // dequeue the pending spawn data from the same allocator as the connect ion. |
72 private final PendingSpawnQueue mPendingSpawnQueue = new PendingSpawnQue ue(); | 72 private final PendingSpawnQueue mPendingSpawnQueue = new PendingSpawnQue ue(); |
73 | 73 |
74 public ChildConnectionAllocator(boolean inSandbox, int numChildServices) { | 74 public ChildConnectionAllocator(boolean inSandbox, int numChildServices, |
75 Class<? extends Service> serviceName) { | |
pkotwicz
2016/06/15 14:13:51
Does {@link serviceName} need to be passed into th
Xi Han
2016/06/15 17:10:40
I understand why you suggest this, but I am feelin
pkotwicz
2016/06/15 17:51:00
I agree, it is good to remove ChildConnectionAlloc
| |
75 mChildProcessConnections = new ChildProcessConnectionImpl[numChildSe rvices]; | 76 mChildProcessConnections = new ChildProcessConnectionImpl[numChildSe rvices]; |
76 mFreeConnectionIndices = new ArrayList<Integer>(numChildServices); | 77 mFreeConnectionIndices = new ArrayList<Integer>(numChildServices); |
77 for (int i = 0; i < numChildServices; i++) { | 78 for (int i = 0; i < numChildServices; i++) { |
78 mFreeConnectionIndices.add(i); | 79 mFreeConnectionIndices.add(i); |
79 } | 80 } |
80 mChildClass = | 81 if (serviceName == null) { |
81 inSandbox ? SandboxedProcessService.class : PrivilegedProces sService.class; | 82 mChildClass = inSandbox ? SandboxedProcessService.class |
83 : PrivilegedProcessService.class; | |
84 } else { | |
85 mChildClass = serviceName; | |
86 } | |
82 mInSandbox = inSandbox; | 87 mInSandbox = inSandbox; |
83 } | 88 } |
84 | 89 |
85 public ChildProcessConnection allocate( | 90 public ChildProcessConnection allocate( |
86 Context context, ChildProcessConnection.DeathCallback deathCallb ack, | 91 Context context, ChildProcessConnection.DeathCallback deathCallb ack, |
87 ChromiumLinkerParams chromiumLinkerParams, | 92 ChromiumLinkerParams chromiumLinkerParams, |
88 boolean alwaysInForeground, | 93 boolean alwaysInForeground, |
89 ChildProcessCreationParams creationParams) { | 94 ChildProcessCreationParams creationParams) { |
90 synchronized (mConnectionLock) { | 95 synchronized (mConnectionLock) { |
91 if (mFreeConnectionIndices.isEmpty()) { | 96 if (mFreeConnectionIndices.isEmpty()) { |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
266 if (numServices < 0) { | 271 if (numServices < 0) { |
267 throw new RuntimeException("Illegal meta data value for number o f child services"); | 272 throw new RuntimeException("Illegal meta data value for number o f child services"); |
268 } | 273 } |
269 return numServices; | 274 return numServices; |
270 } catch (PackageManager.NameNotFoundException e) { | 275 } catch (PackageManager.NameNotFoundException e) { |
271 throw new RuntimeException("Could not get application info"); | 276 throw new RuntimeException("Could not get application info"); |
272 } | 277 } |
273 } | 278 } |
274 | 279 |
275 private static void initConnectionAllocatorsIfNecessary( | 280 private static void initConnectionAllocatorsIfNecessary( |
276 Context context, boolean inSandbox, String packageName) { | 281 Context context, boolean inSandbox, String packageName, |
282 Class<? extends Service> serviceName) { | |
277 // TODO(mariakhomenko): Uses an Object to lock the access. | 283 // TODO(mariakhomenko): Uses an Object to lock the access. |
278 synchronized (ChildProcessLauncher.class) { | 284 synchronized (ChildProcessLauncher.class) { |
279 if (inSandbox) { | 285 if (inSandbox) { |
280 if (sSandboxedChildConnectionAllocatorMap == null) { | 286 if (sSandboxedChildConnectionAllocatorMap == null) { |
281 sSandboxedChildConnectionAllocatorMap = | 287 sSandboxedChildConnectionAllocatorMap = |
282 new ConcurrentHashMap<String, ChildConnectionAllocat or>(); | 288 new ConcurrentHashMap<String, ChildConnectionAllocat or>(); |
283 } | 289 } |
284 if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageNa me)) { | 290 if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageNa me)) { |
285 Log.w(TAG, "Create a new ChildConnectionAllocator with packa ge name = %s," | 291 Log.w(TAG, "Create a new ChildConnectionAllocator with packa ge name = %s," |
286 + " inSandbox = true", | 292 + " inSandbox = true", |
287 packageName); | 293 packageName); |
288 sSandboxedChildConnectionAllocatorMap.put(packageName, | 294 sSandboxedChildConnectionAllocatorMap.put(packageName, |
289 new ChildConnectionAllocator(true, | 295 new ChildConnectionAllocator(true, |
290 getNumberOfServices(context, true, packageNa me))); | 296 getNumberOfServices(context, true, packageNa me), serviceName)); |
291 } | 297 } |
292 } else if (sPrivilegedChildConnectionAllocator == null) { | 298 } else if (sPrivilegedChildConnectionAllocator == null) { |
293 sPrivilegedChildConnectionAllocator = new ChildConnectionAllocat or( | 299 sPrivilegedChildConnectionAllocator = new ChildConnectionAllocat or( |
294 false, getNumberOfServices(context, false, packageName)) ; | 300 false, getNumberOfServices(context, false, packageName), null); |
295 } | 301 } |
296 // TODO(pkotwicz|hanxi): Figure out when old allocators should be re moved from | 302 // TODO(pkotwicz|hanxi): Figure out when old allocators should be re moved from |
297 // {@code sSandboxedChildConnectionAllocatorMap}. | 303 // {@code sSandboxedChildConnectionAllocatorMap}. |
298 } | 304 } |
299 } | 305 } |
300 | 306 |
301 /** | 307 /** |
302 * Note: please make sure that the Allocator has been initialized before cal ling this function. | 308 * Note: please make sure that the Allocator has been initialized before cal ling this function. |
303 * Otherwise, always calls {@link initConnectionAllocatorsIfNecessary} first . | 309 * Otherwise, always calls {@link initConnectionAllocatorsIfNecessary} first . |
304 */ | 310 */ |
305 private static ChildConnectionAllocator getConnectionAllocator( | 311 private static ChildConnectionAllocator getConnectionAllocator( |
306 String packageName, boolean inSandbox) { | 312 String packageName, boolean inSandbox) { |
307 if (!inSandbox) { | 313 if (!inSandbox) { |
308 return sPrivilegedChildConnectionAllocator; | 314 return sPrivilegedChildConnectionAllocator; |
309 } | 315 } |
310 return sSandboxedChildConnectionAllocatorMap.get(packageName); | 316 return sSandboxedChildConnectionAllocatorMap.get(packageName); |
311 } | 317 } |
312 | 318 |
313 /** | 319 /** |
314 * Get the PendingSpawnQueue of the Allocator. Initialize the Allocator if n eeded. | 320 * Get the PendingSpawnQueue of the Allocator. Initialize the Allocator if n eeded. |
315 */ | 321 */ |
316 private static PendingSpawnQueue getPendingSpawnQueue(Context context, Strin g packageName, | 322 private static PendingSpawnQueue getPendingSpawnQueue(Context context, Strin g packageName, |
317 boolean inSandbox) { | 323 boolean inSandbox, Class<? extends Service> serviceName) { |
318 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName); | 324 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName, ser viceName); |
319 return getConnectionAllocator(packageName, inSandbox).getPendingSpawnQue ue(); | 325 return getConnectionAllocator(packageName, inSandbox).getPendingSpawnQue ue(); |
320 } | 326 } |
321 | 327 |
322 private static ChildProcessConnection allocateConnection(Context context, bo olean inSandbox, | 328 private static ChildProcessConnection allocateConnection(Context context, bo olean inSandbox, |
323 ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForegroun d, | 329 ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForegroun d, |
324 ChildProcessCreationParams creationParams) { | 330 ChildProcessCreationParams creationParams) { |
325 ChildProcessConnection.DeathCallback deathCallback = | 331 ChildProcessConnection.DeathCallback deathCallback = |
326 new ChildProcessConnection.DeathCallback() { | 332 new ChildProcessConnection.DeathCallback() { |
327 @Override | 333 @Override |
328 public void onChildProcessDied(ChildProcessConnection connec tion) { | 334 public void onChildProcessDied(ChildProcessConnection connec tion) { |
329 if (connection.getPid() != 0) { | 335 if (connection.getPid() != 0) { |
330 stop(connection.getPid()); | 336 stop(connection.getPid()); |
331 } else { | 337 } else { |
332 freeConnection(connection); | 338 freeConnection(connection); |
333 } | 339 } |
334 } | 340 } |
335 }; | 341 }; |
336 String packageName = creationParams != null ? creationParams.getPackageN ame() | 342 String packageName = creationParams != null ? creationParams.getPackageN ame() |
337 : context.getPackageName(); | 343 : context.getPackageName(); |
338 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName); | 344 Class<? extends Service> serviceName = creationParams != null |
339 return getConnectionAllocator(packageName, inSandbox) | 345 ? creationParams.getServiceName() : null; |
340 .allocate(context, deathCallback, chromiumLinkerParams, alwaysIn Foreground, | 346 initConnectionAllocatorsIfNecessary(context, inSandbox, packageName, ser viceName); |
341 creationParams); | 347 return getConnectionAllocator(packageName, inSandbox).allocate( |
348 context, deathCallback, chromiumLinkerParams, alwaysInForeground , | |
349 creationParams); | |
342 } | 350 } |
343 | 351 |
344 private static boolean sLinkerInitialized = false; | 352 private static boolean sLinkerInitialized = false; |
345 private static long sLinkerLoadAddress = 0; | 353 private static long sLinkerLoadAddress = 0; |
346 | 354 |
347 private static ChromiumLinkerParams getLinkerParamsForNewConnection() { | 355 private static ChromiumLinkerParams getLinkerParamsForNewConnection() { |
348 if (!sLinkerInitialized) { | 356 if (!sLinkerInitialized) { |
349 if (Linker.isUsed()) { | 357 if (Linker.isUsed()) { |
350 sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress(); | 358 sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress(); |
351 if (sLinkerLoadAddress == 0) { | 359 if (sLinkerLoadAddress == 0) { |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
723 synchronized (ChildProcessLauncher.class) { | 731 synchronized (ChildProcessLauncher.class) { |
724 if (inSandbox && sSpareSandboxedConnection != null | 732 if (inSandbox && sSpareSandboxedConnection != null |
725 && sSpareSandboxedConnection.getPackageName().equals(pac kageName)) { | 733 && sSpareSandboxedConnection.getPackageName().equals(pac kageName)) { |
726 allocatedConnection = sSpareSandboxedConnection; | 734 allocatedConnection = sSpareSandboxedConnection; |
727 sSpareSandboxedConnection = null; | 735 sSpareSandboxedConnection = null; |
728 } | 736 } |
729 } | 737 } |
730 if (allocatedConnection == null) { | 738 if (allocatedConnection == null) { |
731 boolean alwaysInForeground = false; | 739 boolean alwaysInForeground = false; |
732 if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true; | 740 if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true; |
741 Class<? extends Service> serviceName = creationParams != null | |
742 ? creationParams.getServiceName() : null; | |
733 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue( | 743 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue( |
734 context, packageName, inSandbox); | 744 context, packageName, inSandbox, serviceName); |
735 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { | 745 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { |
736 allocatedConnection = allocateBoundConnection( | 746 allocatedConnection = allocateBoundConnection( |
737 context, commandLine, inSandbox, alwaysInForeground, creationParams); | 747 context, commandLine, inSandbox, alwaysInForeground, creationParams); |
738 if (allocatedConnection == null) { | 748 if (allocatedConnection == null) { |
739 Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn."); | 749 Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn."); |
740 pendingSpawnQueue.enqueueLocked(new PendingSpawnData(con text, commandLine, | 750 pendingSpawnQueue.enqueueLocked(new PendingSpawnData(con text, commandLine, |
741 childProcessId, filesToBeMapped, clientContext, | 751 childProcessId, filesToBeMapped, clientContext, |
742 callbackType, inSandbox, creationParams)); | 752 callbackType, inSandbox, creationParams)); |
743 return; | 753 return; |
744 } | 754 } |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
920 ChildProcessCreationParams creationParams) { | 930 ChildProcessCreationParams creationParams) { |
921 return allocateBoundConnection(context, null, true, false, creationParam s); | 931 return allocateBoundConnection(context, null, true, false, creationParam s); |
922 } | 932 } |
923 | 933 |
924 /** | 934 /** |
925 * Queue up a spawn requests for testing. | 935 * Queue up a spawn requests for testing. |
926 */ | 936 */ |
927 @VisibleForTesting | 937 @VisibleForTesting |
928 static void enqueuePendingSpawnForTesting(Context context, String[] commandL ine, | 938 static void enqueuePendingSpawnForTesting(Context context, String[] commandL ine, |
929 ChildProcessCreationParams creationParams, boolean inSandbox) { | 939 ChildProcessCreationParams creationParams, boolean inSandbox) { |
940 String packageName = creationParams != null ? creationParams.getPackageN ame() | |
941 : context.getPackageName(); | |
942 Class<? extends Service> serviceName = creationParams != null | |
943 ? creationParams.getServiceName() : null; | |
930 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, | 944 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, |
931 creationParams.getPackageName(), inSandbox); | 945 packageName, inSandbox, serviceName); |
932 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { | 946 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { |
933 pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, comman dLine, 1, | 947 pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, comman dLine, 1, |
934 new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true, | 948 new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true, |
935 creationParams)); | 949 creationParams)); |
936 } | 950 } |
937 } | 951 } |
938 | 952 |
939 /** | 953 /** |
940 * @return the number of sandboxed connections of given {@link packageName} managed by the | 954 * @return the number of sandboxed connections of given {@link packageName} managed by the |
941 * allocator. | 955 * allocator. |
942 */ | 956 */ |
943 @VisibleForTesting | 957 @VisibleForTesting |
944 static int allocatedSandboxedConnectionsCountForTesting(Context context, Str ing packageName) { | 958 static int allocatedSandboxedConnectionsCountForTesting(Context context, Str ing packageName) { |
945 initConnectionAllocatorsIfNecessary(context, true, packageName); | 959 initConnectionAllocatorsIfNecessary(context, true, packageName, null); |
946 return sSandboxedChildConnectionAllocatorMap.get(packageName) | 960 return sSandboxedChildConnectionAllocatorMap.get(packageName) |
947 .allocatedConnectionsCountForTesting(); | 961 .allocatedConnectionsCountForTesting(); |
948 } | 962 } |
949 | 963 |
950 /** @return the count of services set up and working */ | 964 /** @return the count of services set up and working */ |
951 @VisibleForTesting | 965 @VisibleForTesting |
952 static int connectedServicesCountForTesting() { | 966 static int connectedServicesCountForTesting() { |
953 return sServiceMap.size(); | 967 return sServiceMap.size(); |
954 } | 968 } |
955 | 969 |
956 /** | 970 /** |
957 * @param context The context. | 971 * @param context The context. |
958 * @param packageName The package name of the {@link ChildProcessAlocator}. | 972 * @param packageName The package name of the {@link ChildProcessAlocator}. |
959 * @param inSandbox Whether the connection is sandboxed. | 973 * @param inSandbox Whether the connection is sandboxed. |
960 * @return the count of pending spawns in the queue. | 974 * @return the count of pending spawns in the queue. |
961 */ | 975 */ |
962 @VisibleForTesting | 976 @VisibleForTesting |
963 static int pendingSpawnsCountForTesting(Context context, String packageName, | 977 static int pendingSpawnsCountForTesting(Context context, String packageName, |
964 boolean inSandbox) { | 978 boolean inSandbox) { |
965 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, pack ageName, inSandbox); | 979 PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, pack ageName, inSandbox, |
980 null); | |
966 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { | 981 synchronized (pendingSpawnQueue.mPendingSpawnsLock) { |
967 return pendingSpawnQueue.sizeLocked(); | 982 return pendingSpawnQueue.sizeLocked(); |
968 } | 983 } |
969 } | 984 } |
970 | 985 |
971 /** | 986 /** |
972 * Kills the child process for testing. | 987 * Kills the child process for testing. |
973 * @return true iff the process was killed as expected | 988 * @return true iff the process was killed as expected |
974 */ | 989 */ |
975 @VisibleForTesting | 990 @VisibleForTesting |
976 public static boolean crashProcessForTesting(int pid) { | 991 public static boolean crashProcessForTesting(int pid) { |
977 if (sServiceMap.get(pid) == null) return false; | 992 if (sServiceMap.get(pid) == null) return false; |
978 | 993 |
979 try { | 994 try { |
980 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); | 995 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); |
981 } catch (RemoteException ex) { | 996 } catch (RemoteException ex) { |
982 return false; | 997 return false; |
983 } | 998 } |
984 | 999 |
985 return true; | 1000 return true; |
986 } | 1001 } |
987 | 1002 |
988 private static native void nativeOnChildProcessStarted(long clientContext, i nt pid); | 1003 private static native void nativeOnChildProcessStarted(long clientContext, i nt pid); |
989 private static native void nativeEstablishSurfacePeer( | 1004 private static native void nativeEstablishSurfacePeer( |
990 int pid, Surface surface, int primaryID, int secondaryID); | 1005 int pid, Surface surface, int primaryID, int secondaryID); |
991 private static native boolean nativeIsSingleProcess(); | 1006 private static native boolean nativeIsSingleProcess(); |
992 } | 1007 } |
OLD | NEW |