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