1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.os; 18 19import static android.system.OsConstants.POLLIN; 20import static android.system.OsConstants.S_IRWXG; 21import static android.system.OsConstants.S_IRWXO; 22 23import android.content.res.Resources; 24import android.content.res.TypedArray; 25import android.icu.impl.CacheValue; 26import android.icu.text.DecimalFormatSymbols; 27import android.icu.util.ULocale; 28import android.net.LocalServerSocket; 29import android.opengl.EGL14; 30import android.os.Process; 31import android.os.SystemClock; 32import android.os.SystemProperties; 33import android.os.Trace; 34import android.security.keystore.AndroidKeyStoreProvider; 35import android.system.ErrnoException; 36import android.system.Os; 37import android.system.OsConstants; 38import android.system.StructPollfd; 39import android.text.Hyphenator; 40import android.util.EventLog; 41import android.util.Log; 42import android.webkit.WebViewFactory; 43import android.widget.TextView; 44 45import com.android.internal.os.InstallerConnection.InstallerException; 46 47import dalvik.system.DexFile; 48import dalvik.system.PathClassLoader; 49import dalvik.system.VMRuntime; 50import dalvik.system.ZygoteHooks; 51 52import libcore.io.IoUtils; 53 54import java.io.BufferedReader; 55import java.io.FileDescriptor; 56import java.io.FileInputStream; 57import java.io.FileNotFoundException; 58import java.io.IOException; 59import java.io.InputStream; 60import java.io.InputStreamReader; 61import java.lang.reflect.InvocationTargetException; 62import java.lang.reflect.Method; 63import java.security.Security; 64import java.security.Provider; 65import java.util.ArrayList; 66 67/** 68 * Startup class for the zygote process. 69 * 70 * Pre-initializes some classes, and then waits for commands on a UNIX domain 71 * socket. Based on these commands, forks off child processes that inherit 72 * the initial state of the VM. 73 * 74 * Please see {@link ZygoteConnection.Arguments} for documentation on the 75 * client protocol. 76 * 77 * @hide 78 */ 79public class ZygoteInit { 80 private static final String TAG = "Zygote"; 81 82 private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; 83 private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container"; 84 85 private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; 86 87 private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; 88 private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; 89 90 /** when preloading, GC after allocating this many bytes */ 91 private static final int PRELOAD_GC_THRESHOLD = 50000; 92 93 private static final String ABI_LIST_ARG = "--abi-list="; 94 95 private static final String SOCKET_NAME_ARG = "--socket-name="; 96 97 private static LocalServerSocket sServerSocket; 98 99 /** 100 * Used to pre-load resources. We hold a global reference on it so it 101 * never gets destroyed. 102 */ 103 private static Resources mResources; 104 105 /** 106 * The path of a file that contains classes to preload. 107 */ 108 private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes"; 109 110 /** Controls whether we should preload resources during zygote init. */ 111 public static final boolean PRELOAD_RESOURCES = true; 112 113 /** 114 * Registers a server socket for zygote command connections 115 * 116 * @throws RuntimeException when open fails 117 */ 118 private static void registerZygoteSocket(String socketName) { 119 if (sServerSocket == null) { 120 int fileDesc; 121 final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; 122 try { 123 String env = System.getenv(fullSocketName); 124 fileDesc = Integer.parseInt(env); 125 } catch (RuntimeException ex) { 126 throw new RuntimeException(fullSocketName + " unset or invalid", ex); 127 } 128 129 try { 130 FileDescriptor fd = new FileDescriptor(); 131 fd.setInt$(fileDesc); 132 sServerSocket = new LocalServerSocket(fd); 133 } catch (IOException ex) { 134 throw new RuntimeException( 135 "Error binding to local socket '" + fileDesc + "'", ex); 136 } 137 } 138 } 139 140 /** 141 * Waits for and accepts a single command connection. Throws 142 * RuntimeException on failure. 143 */ 144 private static ZygoteConnection acceptCommandPeer(String abiList) { 145 try { 146 return new ZygoteConnection(sServerSocket.accept(), abiList); 147 } catch (IOException ex) { 148 throw new RuntimeException( 149 "IOException during accept()", ex); 150 } 151 } 152 153 /** 154 * Close and clean up zygote sockets. Called on shutdown and on the 155 * child's exit path. 156 */ 157 static void closeServerSocket() { 158 try { 159 if (sServerSocket != null) { 160 FileDescriptor fd = sServerSocket.getFileDescriptor(); 161 sServerSocket.close(); 162 if (fd != null) { 163 Os.close(fd); 164 } 165 } 166 } catch (IOException ex) { 167 Log.e(TAG, "Zygote: error closing sockets", ex); 168 } catch (ErrnoException ex) { 169 Log.e(TAG, "Zygote: error closing descriptor", ex); 170 } 171 172 sServerSocket = null; 173 } 174 175 /** 176 * Return the server socket's underlying file descriptor, so that 177 * ZygoteConnection can pass it to the native code for proper 178 * closure after a child process is forked off. 179 */ 180 181 static FileDescriptor getServerSocketFileDescriptor() { 182 return sServerSocket.getFileDescriptor(); 183 } 184 185 private static final int UNPRIVILEGED_UID = 9999; 186 private static final int UNPRIVILEGED_GID = 9999; 187 188 private static final int ROOT_UID = 0; 189 private static final int ROOT_GID = 0; 190 191 static void preload() { 192 Log.d(TAG, "begin preload"); 193 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning"); 194 beginIcuCachePinning(); 195 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 196 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses"); 197 preloadClasses(); 198 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 199 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources"); 200 preloadResources(); 201 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 202 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL"); 203 preloadOpenGL(); 204 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 205 preloadSharedLibraries(); 206 preloadTextResources(); 207 // Ask the WebViewFactory to do any initialization that must run in the zygote process, 208 // for memory sharing purposes. 209 WebViewFactory.prepareWebViewInZygote(); 210 endIcuCachePinning(); 211 warmUpJcaProviders(); 212 Log.d(TAG, "end preload"); 213 } 214 215 private static void beginIcuCachePinning() { 216 // Pin ICU data in memory from this point that would normally be held by soft references. 217 // Without this, any references created immediately below or during class preloading 218 // would be collected when the Zygote GC runs in gcAndFinalize(). 219 Log.i(TAG, "Installing ICU cache reference pinning..."); 220 221 CacheValue.setStrength(CacheValue.Strength.STRONG); 222 223 Log.i(TAG, "Preloading ICU data..."); 224 // Explicitly exercise code to cache data apps are likely to need. 225 ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() }; 226 for (ULocale uLocale : localesToPin) { 227 new DecimalFormatSymbols(uLocale); 228 } 229 } 230 231 private static void endIcuCachePinning() { 232 // All cache references created by ICU from this point will be soft. 233 CacheValue.setStrength(CacheValue.Strength.SOFT); 234 235 Log.i(TAG, "Uninstalled ICU cache reference pinning..."); 236 } 237 238 private static void preloadSharedLibraries() { 239 Log.i(TAG, "Preloading shared libraries..."); 240 System.loadLibrary("android"); 241 System.loadLibrary("compiler_rt"); 242 System.loadLibrary("jnigraphics"); 243 } 244 245 private static void preloadOpenGL() { 246 if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) { 247 EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 248 } 249 } 250 251 private static void preloadTextResources() { 252 Hyphenator.init(); 253 TextView.preloadFontCache(); 254 } 255 256 /** 257 * Register AndroidKeyStoreProvider and warm up the providers that are already registered. 258 * 259 * By doing it here we avoid that each app does it when requesting a service from the 260 * provider for the first time. 261 */ 262 private static void warmUpJcaProviders() { 263 long startTime = SystemClock.uptimeMillis(); 264 Trace.traceBegin( 265 Trace.TRACE_TAG_DALVIK, "Starting installation of AndroidKeyStoreProvider"); 266 // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert 267 // preferred providers. Note this is not done via security.properties as the JCA providers 268 // are not on the classpath in the case of, for example, raw dalvikvm runtimes. 269 AndroidKeyStoreProvider.install(); 270 Log.i(TAG, "Installed AndroidKeyStoreProvider in " 271 + (SystemClock.uptimeMillis() - startTime) + "ms."); 272 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 273 274 startTime = SystemClock.uptimeMillis(); 275 Trace.traceBegin( 276 Trace.TRACE_TAG_DALVIK, "Starting warm up of JCA providers"); 277 for (Provider p : Security.getProviders()) { 278 p.warmUpServiceProvision(); 279 } 280 Log.i(TAG, "Warmed up JCA providers in " 281 + (SystemClock.uptimeMillis() - startTime) + "ms."); 282 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 283 } 284 285 /** 286 * Performs Zygote process initialization. Loads and initializes 287 * commonly used classes. 288 * 289 * Most classes only cause a few hundred bytes to be allocated, but 290 * a few will allocate a dozen Kbytes (in one case, 500+K). 291 */ 292 private static void preloadClasses() { 293 final VMRuntime runtime = VMRuntime.getRuntime(); 294 295 InputStream is; 296 try { 297 is = new FileInputStream(PRELOADED_CLASSES); 298 } catch (FileNotFoundException e) { 299 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "."); 300 return; 301 } 302 303 Log.i(TAG, "Preloading classes..."); 304 long startTime = SystemClock.uptimeMillis(); 305 306 // Drop root perms while running static initializers. 307 final int reuid = Os.getuid(); 308 final int regid = Os.getgid(); 309 310 // We need to drop root perms only if we're already root. In the case of "wrapped" 311 // processes (see WrapperInit), this function is called from an unprivileged uid 312 // and gid. 313 boolean droppedPriviliges = false; 314 if (reuid == ROOT_UID && regid == ROOT_GID) { 315 try { 316 Os.setregid(ROOT_GID, UNPRIVILEGED_GID); 317 Os.setreuid(ROOT_UID, UNPRIVILEGED_UID); 318 } catch (ErrnoException ex) { 319 throw new RuntimeException("Failed to drop root", ex); 320 } 321 322 droppedPriviliges = true; 323 } 324 325 // Alter the target heap utilization. With explicit GCs this 326 // is not likely to have any effect. 327 float defaultUtilization = runtime.getTargetHeapUtilization(); 328 runtime.setTargetHeapUtilization(0.8f); 329 330 try { 331 BufferedReader br 332 = new BufferedReader(new InputStreamReader(is), 256); 333 334 int count = 0; 335 String line; 336 while ((line = br.readLine()) != null) { 337 // Skip comments and blank lines. 338 line = line.trim(); 339 if (line.startsWith("#") || line.equals("")) { 340 continue; 341 } 342 343 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClass " + line); 344 try { 345 if (false) { 346 Log.v(TAG, "Preloading " + line + "..."); 347 } 348 // Load and explicitly initialize the given class. Use 349 // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups 350 // (to derive the caller's class-loader). Use true to force initialization, and 351 // null for the boot classpath class-loader (could as well cache the 352 // class-loader of this class in a variable). 353 Class.forName(line, true, null); 354 count++; 355 } catch (ClassNotFoundException e) { 356 Log.w(TAG, "Class not found for preloading: " + line); 357 } catch (UnsatisfiedLinkError e) { 358 Log.w(TAG, "Problem preloading " + line + ": " + e); 359 } catch (Throwable t) { 360 Log.e(TAG, "Error preloading " + line + ".", t); 361 if (t instanceof Error) { 362 throw (Error) t; 363 } 364 if (t instanceof RuntimeException) { 365 throw (RuntimeException) t; 366 } 367 throw new RuntimeException(t); 368 } 369 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 370 } 371 372 Log.i(TAG, "...preloaded " + count + " classes in " 373 + (SystemClock.uptimeMillis()-startTime) + "ms."); 374 } catch (IOException e) { 375 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e); 376 } finally { 377 IoUtils.closeQuietly(is); 378 // Restore default. 379 runtime.setTargetHeapUtilization(defaultUtilization); 380 381 // Fill in dex caches with classes, fields, and methods brought in by preloading. 382 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches"); 383 runtime.preloadDexCaches(); 384 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 385 386 // Bring back root. We'll need it later if we're in the zygote. 387 if (droppedPriviliges) { 388 try { 389 Os.setreuid(ROOT_UID, ROOT_UID); 390 Os.setregid(ROOT_GID, ROOT_GID); 391 } catch (ErrnoException ex) { 392 throw new RuntimeException("Failed to restore root", ex); 393 } 394 } 395 } 396 } 397 398 /** 399 * Load in commonly used resources, so they can be shared across 400 * processes. 401 * 402 * These tend to be a few Kbytes, but are frequently in the 20-40K 403 * range, and occasionally even larger. 404 */ 405 private static void preloadResources() { 406 final VMRuntime runtime = VMRuntime.getRuntime(); 407 408 try { 409 mResources = Resources.getSystem(); 410 mResources.startPreloading(); 411 if (PRELOAD_RESOURCES) { 412 Log.i(TAG, "Preloading resources..."); 413 414 long startTime = SystemClock.uptimeMillis(); 415 TypedArray ar = mResources.obtainTypedArray( 416 com.android.internal.R.array.preloaded_drawables); 417 int N = preloadDrawables(ar); 418 ar.recycle(); 419 Log.i(TAG, "...preloaded " + N + " resources in " 420 + (SystemClock.uptimeMillis()-startTime) + "ms."); 421 422 startTime = SystemClock.uptimeMillis(); 423 ar = mResources.obtainTypedArray( 424 com.android.internal.R.array.preloaded_color_state_lists); 425 N = preloadColorStateLists(ar); 426 ar.recycle(); 427 Log.i(TAG, "...preloaded " + N + " resources in " 428 + (SystemClock.uptimeMillis()-startTime) + "ms."); 429 430 if (mResources.getBoolean( 431 com.android.internal.R.bool.config_freeformWindowManagement)) { 432 startTime = SystemClock.uptimeMillis(); 433 ar = mResources.obtainTypedArray( 434 com.android.internal.R.array.preloaded_freeform_multi_window_drawables); 435 N = preloadDrawables(ar); 436 ar.recycle(); 437 Log.i(TAG, "...preloaded " + N + " resource in " 438 + (SystemClock.uptimeMillis() - startTime) + "ms."); 439 } 440 } 441 mResources.finishPreloading(); 442 } catch (RuntimeException e) { 443 Log.w(TAG, "Failure preloading resources", e); 444 } 445 } 446 447 private static int preloadColorStateLists(TypedArray ar) { 448 int N = ar.length(); 449 for (int i=0; i<N; i++) { 450 int id = ar.getResourceId(i, 0); 451 if (false) { 452 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); 453 } 454 if (id != 0) { 455 if (mResources.getColorStateList(id, null) == null) { 456 throw new IllegalArgumentException( 457 "Unable to find preloaded color resource #0x" 458 + Integer.toHexString(id) 459 + " (" + ar.getString(i) + ")"); 460 } 461 } 462 } 463 return N; 464 } 465 466 467 private static int preloadDrawables(TypedArray ar) { 468 int N = ar.length(); 469 for (int i=0; i<N; i++) { 470 int id = ar.getResourceId(i, 0); 471 if (false) { 472 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); 473 } 474 if (id != 0) { 475 if (mResources.getDrawable(id, null) == null) { 476 throw new IllegalArgumentException( 477 "Unable to find preloaded drawable resource #0x" 478 + Integer.toHexString(id) 479 + " (" + ar.getString(i) + ")"); 480 } 481 } 482 } 483 return N; 484 } 485 486 /** 487 * Runs several special GCs to try to clean up a few generations of 488 * softly- and final-reachable objects, along with any other garbage. 489 * This is only useful just before a fork(). 490 */ 491 /*package*/ static void gcAndFinalize() { 492 final VMRuntime runtime = VMRuntime.getRuntime(); 493 494 /* runFinalizationSync() lets finalizers be called in Zygote, 495 * which doesn't have a HeapWorker thread. 496 */ 497 System.gc(); 498 runtime.runFinalizationSync(); 499 System.gc(); 500 } 501 502 /** 503 * Finish remaining work for the newly forked system server process. 504 */ 505 private static void handleSystemServerProcess( 506 ZygoteConnection.Arguments parsedArgs) 507 throws ZygoteInit.MethodAndArgsCaller { 508 509 closeServerSocket(); 510 511 // set umask to 0077 so new files and directories will default to owner-only permissions. 512 Os.umask(S_IRWXG | S_IRWXO); 513 514 if (parsedArgs.niceName != null) { 515 Process.setArgV0(parsedArgs.niceName); 516 } 517 518 final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); 519 if (systemServerClasspath != null) { 520 performSystemServerDexOpt(systemServerClasspath); 521 } 522 523 if (parsedArgs.invokeWith != null) { 524 String[] args = parsedArgs.remainingArgs; 525 // If we have a non-null system server class path, we'll have to duplicate the 526 // existing arguments and append the classpath to it. ART will handle the classpath 527 // correctly when we exec a new process. 528 if (systemServerClasspath != null) { 529 String[] amendedArgs = new String[args.length + 2]; 530 amendedArgs[0] = "-cp"; 531 amendedArgs[1] = systemServerClasspath; 532 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length); 533 } 534 535 WrapperInit.execApplication(parsedArgs.invokeWith, 536 parsedArgs.niceName, parsedArgs.targetSdkVersion, 537 VMRuntime.getCurrentInstructionSet(), null, args); 538 } else { 539 ClassLoader cl = null; 540 if (systemServerClasspath != null) { 541 cl = createSystemServerClassLoader(systemServerClasspath, 542 parsedArgs.targetSdkVersion); 543 544 Thread.currentThread().setContextClassLoader(cl); 545 } 546 547 /* 548 * Pass the remaining arguments to SystemServer. 549 */ 550 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); 551 } 552 553 /* should never reach here */ 554 } 555 556 /** 557 * Creates a PathClassLoader for the system server. It also creates 558 * a shared namespace associated with the classloader to let it access 559 * platform-private native libraries. 560 */ 561 private static PathClassLoader createSystemServerClassLoader(String systemServerClasspath, 562 int targetSdkVersion) { 563 String librarySearchPath = System.getProperty("java.library.path"); 564 565 return PathClassLoaderFactory.createClassLoader(systemServerClasspath, 566 librarySearchPath, 567 null /* libraryPermittedPath */, 568 ClassLoader.getSystemClassLoader(), 569 targetSdkVersion, 570 true /* isNamespaceShared */); 571 } 572 573 /** 574 * Performs dex-opt on the elements of {@code classPath}, if needed. We 575 * choose the instruction set of the current runtime. 576 */ 577 private static void performSystemServerDexOpt(String classPath) { 578 final String[] classPathElements = classPath.split(":"); 579 final InstallerConnection installer = new InstallerConnection(); 580 installer.waitForConnection(); 581 final String instructionSet = VMRuntime.getRuntime().vmInstructionSet(); 582 583 try { 584 String sharedLibraries = ""; 585 for (String classPathElement : classPathElements) { 586 // System server is fully AOTed and never profiled 587 // for profile guided compilation. 588 // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING? 589 final int dexoptNeeded = DexFile.getDexOptNeeded( 590 classPathElement, instructionSet, "speed", 591 false /* newProfile */); 592 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { 593 installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet, 594 dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/, 595 sharedLibraries); 596 } 597 if (!sharedLibraries.isEmpty()) { 598 sharedLibraries += ":"; 599 } 600 sharedLibraries += classPathElement; 601 } 602 } catch (IOException | InstallerException e) { 603 throw new RuntimeException("Error starting system_server", e); 604 } finally { 605 installer.disconnect(); 606 } 607 } 608 609 /** 610 * Prepare the arguments and fork for the system server process. 611 */ 612 private static boolean startSystemServer(String abiList, String socketName) 613 throws MethodAndArgsCaller, RuntimeException { 614 long capabilities = posixCapabilitiesAsBits( 615 OsConstants.CAP_IPC_LOCK, 616 OsConstants.CAP_KILL, 617 OsConstants.CAP_NET_ADMIN, 618 OsConstants.CAP_NET_BIND_SERVICE, 619 OsConstants.CAP_NET_BROADCAST, 620 OsConstants.CAP_NET_RAW, 621 OsConstants.CAP_SYS_MODULE, 622 OsConstants.CAP_SYS_NICE, 623 OsConstants.CAP_SYS_RESOURCE, 624 OsConstants.CAP_SYS_TIME, 625 OsConstants.CAP_SYS_TTY_CONFIG 626 ); 627 /* Containers run without this capability, so avoid setting it in that case */ 628 if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) { 629 capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND); 630 } 631 /* Hardcoded command line to start the system server */ 632 String args[] = { 633 "--setuid=1000", 634 "--setgid=1000", 635 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010", 636 "--capabilities=" + capabilities + "," + capabilities, 637 "--nice-name=system_server", 638 "--runtime-args", 639 "com.android.server.SystemServer", 640 }; 641 ZygoteConnection.Arguments parsedArgs = null; 642 643 int pid; 644 645 try { 646 parsedArgs = new ZygoteConnection.Arguments(args); 647 ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); 648 ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); 649 650 /* Request to fork the system server process */ 651 pid = Zygote.forkSystemServer( 652 parsedArgs.uid, parsedArgs.gid, 653 parsedArgs.gids, 654 parsedArgs.debugFlags, 655 null, 656 parsedArgs.permittedCapabilities, 657 parsedArgs.effectiveCapabilities); 658 } catch (IllegalArgumentException ex) { 659 throw new RuntimeException(ex); 660 } 661 662 /* For child process */ 663 if (pid == 0) { 664 if (hasSecondZygote(abiList)) { 665 waitForSecondaryZygote(socketName); 666 } 667 668 handleSystemServerProcess(parsedArgs); 669 } 670 671 return true; 672 } 673 674 /** 675 * Gets the bit array representation of the provided list of POSIX capabilities. 676 */ 677 private static long posixCapabilitiesAsBits(int... capabilities) { 678 long result = 0; 679 for (int capability : capabilities) { 680 if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) { 681 throw new IllegalArgumentException(String.valueOf(capability)); 682 } 683 result |= (1L << capability); 684 } 685 return result; 686 } 687 688 public static void main(String argv[]) { 689 // Mark zygote start. This ensures that thread creation will throw 690 // an error. 691 ZygoteHooks.startZygoteNoThreadCreation(); 692 693 try { 694 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit"); 695 RuntimeInit.enableDdms(); 696 // Start profiling the zygote initialization. 697 SamplingProfilerIntegration.start(); 698 699 boolean startSystemServer = false; 700 String socketName = "zygote"; 701 String abiList = null; 702 for (int i = 1; i < argv.length; i++) { 703 if ("start-system-server".equals(argv[i])) { 704 startSystemServer = true; 705 } else if (argv[i].startsWith(ABI_LIST_ARG)) { 706 abiList = argv[i].substring(ABI_LIST_ARG.length()); 707 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { 708 socketName = argv[i].substring(SOCKET_NAME_ARG.length()); 709 } else { 710 throw new RuntimeException("Unknown command line argument: " + argv[i]); 711 } 712 } 713 714 if (abiList == null) { 715 throw new RuntimeException("No ABI list supplied."); 716 } 717 718 registerZygoteSocket(socketName); 719 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload"); 720 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, 721 SystemClock.uptimeMillis()); 722 preload(); 723 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, 724 SystemClock.uptimeMillis()); 725 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 726 727 // Finish profiling the zygote initialization. 728 SamplingProfilerIntegration.writeZygoteSnapshot(); 729 730 // Do an initial gc to clean up after startup 731 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC"); 732 gcAndFinalize(); 733 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 734 735 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 736 737 // Disable tracing so that forked processes do not inherit stale tracing tags from 738 // Zygote. 739 Trace.setTracingEnabled(false); 740 741 // Zygote process unmounts root storage spaces. 742 Zygote.nativeUnmountStorageOnInit(); 743 744 ZygoteHooks.stopZygoteNoThreadCreation(); 745 746 if (startSystemServer) { 747 startSystemServer(abiList, socketName); 748 } 749 750 Log.i(TAG, "Accepting command socket connections"); 751 runSelectLoop(abiList); 752 753 closeServerSocket(); 754 } catch (MethodAndArgsCaller caller) { 755 caller.run(); 756 } catch (RuntimeException ex) { 757 Log.e(TAG, "Zygote died with exception", ex); 758 closeServerSocket(); 759 throw ex; 760 } 761 } 762 763 /** 764 * Return {@code true} if this device configuration has another zygote. 765 * 766 * We determine this by comparing the device ABI list with this zygotes 767 * list. If this zygote supports all ABIs this device supports, there won't 768 * be another zygote. 769 */ 770 private static boolean hasSecondZygote(String abiList) { 771 return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList); 772 } 773 774 private static void waitForSecondaryZygote(String socketName) { 775 String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ? 776 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; 777 while (true) { 778 try { 779 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); 780 zs.close(); 781 break; 782 } catch (IOException ioe) { 783 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); 784 } 785 786 try { 787 Thread.sleep(1000); 788 } catch (InterruptedException ie) { 789 } 790 } 791 } 792 793 /** 794 * Runs the zygote process's select loop. Accepts new connections as 795 * they happen, and reads commands from connections one spawn-request's 796 * worth at a time. 797 * 798 * @throws MethodAndArgsCaller in a child process when a main() should 799 * be executed. 800 */ 801 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller { 802 ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); 803 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); 804 805 fds.add(sServerSocket.getFileDescriptor()); 806 peers.add(null); 807 808 while (true) { 809 StructPollfd[] pollFds = new StructPollfd[fds.size()]; 810 for (int i = 0; i < pollFds.length; ++i) { 811 pollFds[i] = new StructPollfd(); 812 pollFds[i].fd = fds.get(i); 813 pollFds[i].events = (short) POLLIN; 814 } 815 try { 816 Os.poll(pollFds, -1); 817 } catch (ErrnoException ex) { 818 throw new RuntimeException("poll failed", ex); 819 } 820 for (int i = pollFds.length - 1; i >= 0; --i) { 821 if ((pollFds[i].revents & POLLIN) == 0) { 822 continue; 823 } 824 if (i == 0) { 825 ZygoteConnection newPeer = acceptCommandPeer(abiList); 826 peers.add(newPeer); 827 fds.add(newPeer.getFileDesciptor()); 828 } else { 829 boolean done = peers.get(i).runOnce(); 830 if (done) { 831 peers.remove(i); 832 fds.remove(i); 833 } 834 } 835 } 836 } 837 } 838 839 /** 840 * Class not instantiable. 841 */ 842 private ZygoteInit() { 843 } 844 845 /** 846 * Helper exception class which holds a method and arguments and 847 * can call them. This is used as part of a trampoline to get rid of 848 * the initial process setup stack frames. 849 */ 850 public static class MethodAndArgsCaller extends Exception 851 implements Runnable { 852 /** method to call */ 853 private final Method mMethod; 854 855 /** argument array */ 856 private final String[] mArgs; 857 858 public MethodAndArgsCaller(Method method, String[] args) { 859 mMethod = method; 860 mArgs = args; 861 } 862 863 public void run() { 864 try { 865 mMethod.invoke(null, new Object[] { mArgs }); 866 } catch (IllegalAccessException ex) { 867 throw new RuntimeException(ex); 868 } catch (InvocationTargetException ex) { 869 Throwable cause = ex.getCause(); 870 if (cause instanceof RuntimeException) { 871 throw (RuntimeException) cause; 872 } else if (cause instanceof Error) { 873 throw (Error) cause; 874 } 875 throw new RuntimeException(ex); 876 } 877 } 878 } 879} 880