Home | History | Annotate | Download | only in os
      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.S_IRWXG;
     20 import static android.system.OsConstants.S_IRWXO;
     21 
     22 import android.content.res.Resources;
     23 import android.content.res.TypedArray;
     24 import android.net.LocalServerSocket;
     25 import android.opengl.EGL14;
     26 import android.os.Build;
     27 import android.os.Debug;
     28 import android.os.Process;
     29 import android.os.SystemClock;
     30 import android.os.SystemProperties;
     31 import android.os.Trace;
     32 import android.system.ErrnoException;
     33 import android.system.Os;
     34 import android.system.OsConstants;
     35 import android.util.EventLog;
     36 import android.util.Log;
     37 import android.util.Slog;
     38 import android.webkit.WebViewFactory;
     39 
     40 import dalvik.system.DexFile;
     41 import dalvik.system.PathClassLoader;
     42 import dalvik.system.VMRuntime;
     43 
     44 import libcore.io.IoUtils;
     45 
     46 import java.io.BufferedReader;
     47 import java.io.FileDescriptor;
     48 import java.io.FileInputStream;
     49 import java.io.FileNotFoundException;
     50 import java.io.IOException;
     51 import java.io.InputStream;
     52 import java.io.InputStreamReader;
     53 import java.lang.reflect.InvocationTargetException;
     54 import java.lang.reflect.Method;
     55 import java.lang.reflect.Modifier;
     56 import java.util.ArrayList;
     57 
     58 /**
     59  * Startup class for the zygote process.
     60  *
     61  * Pre-initializes some classes, and then waits for commands on a UNIX domain
     62  * socket. Based on these commands, forks off child processes that inherit
     63  * the initial state of the VM.
     64  *
     65  * Please see {@link ZygoteConnection.Arguments} for documentation on the
     66  * client protocol.
     67  *
     68  * @hide
     69  */
     70 public class ZygoteInit {
     71     private static final String TAG = "Zygote";
     72 
     73     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
     74 
     75     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
     76 
     77     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
     78     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
     79 
     80     /** when preloading, GC after allocating this many bytes */
     81     private static final int PRELOAD_GC_THRESHOLD = 50000;
     82 
     83     private static final String ABI_LIST_ARG = "--abi-list=";
     84 
     85     private static final String SOCKET_NAME_ARG = "--socket-name=";
     86 
     87     private static LocalServerSocket sServerSocket;
     88 
     89     /**
     90      * Used to pre-load resources.  We hold a global reference on it so it
     91      * never gets destroyed.
     92      */
     93     private static Resources mResources;
     94 
     95     /**
     96      * The number of times that the main Zygote loop
     97      * should run before calling gc() again.
     98      */
     99     static final int GC_LOOP_COUNT = 10;
    100 
    101     /**
    102      * The path of a file that contains classes to preload.
    103      */
    104     private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
    105 
    106     /** Controls whether we should preload resources during zygote init. */
    107     private static final boolean PRELOAD_RESOURCES = true;
    108 
    109     /**
    110      * Invokes a static "main(argv[]) method on class "className".
    111      * Converts various failing exceptions into RuntimeExceptions, with
    112      * the assumption that they will then cause the VM instance to exit.
    113      *
    114      * @param loader class loader to use
    115      * @param className Fully-qualified class name
    116      * @param argv Argument vector for main()
    117      */
    118     static void invokeStaticMain(ClassLoader loader,
    119             String className, String[] argv)
    120             throws ZygoteInit.MethodAndArgsCaller {
    121         Class<?> cl;
    122 
    123         try {
    124             cl = loader.loadClass(className);
    125         } catch (ClassNotFoundException ex) {
    126             throw new RuntimeException(
    127                     "Missing class when invoking static main " + className,
    128                     ex);
    129         }
    130 
    131         Method m;
    132         try {
    133             m = cl.getMethod("main", new Class[] { String[].class });
    134         } catch (NoSuchMethodException ex) {
    135             throw new RuntimeException(
    136                     "Missing static main on " + className, ex);
    137         } catch (SecurityException ex) {
    138             throw new RuntimeException(
    139                     "Problem getting static main on " + className, ex);
    140         }
    141 
    142         int modifiers = m.getModifiers();
    143         if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
    144             throw new RuntimeException(
    145                     "Main method is not public and static on " + className);
    146         }
    147 
    148         /*
    149          * This throw gets caught in ZygoteInit.main(), which responds
    150          * by invoking the exception's run() method. This arrangement
    151          * clears up all the stack frames that were required in setting
    152          * up the process.
    153          */
    154         throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    155     }
    156 
    157     /**
    158      * Registers a server socket for zygote command connections
    159      *
    160      * @throws RuntimeException when open fails
    161      */
    162     private static void registerZygoteSocket(String socketName) {
    163         if (sServerSocket == null) {
    164             int fileDesc;
    165             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
    166             try {
    167                 String env = System.getenv(fullSocketName);
    168                 fileDesc = Integer.parseInt(env);
    169             } catch (RuntimeException ex) {
    170                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
    171             }
    172 
    173             try {
    174                 sServerSocket = new LocalServerSocket(
    175                         createFileDescriptor(fileDesc));
    176             } catch (IOException ex) {
    177                 throw new RuntimeException(
    178                         "Error binding to local socket '" + fileDesc + "'", ex);
    179             }
    180         }
    181     }
    182 
    183     /**
    184      * Waits for and accepts a single command connection. Throws
    185      * RuntimeException on failure.
    186      */
    187     private static ZygoteConnection acceptCommandPeer(String abiList) {
    188         try {
    189             return new ZygoteConnection(sServerSocket.accept(), abiList);
    190         } catch (IOException ex) {
    191             throw new RuntimeException(
    192                     "IOException during accept()", ex);
    193         }
    194     }
    195 
    196     /**
    197      * Close and clean up zygote sockets. Called on shutdown and on the
    198      * child's exit path.
    199      */
    200     static void closeServerSocket() {
    201         try {
    202             if (sServerSocket != null) {
    203                 FileDescriptor fd = sServerSocket.getFileDescriptor();
    204                 sServerSocket.close();
    205                 if (fd != null) {
    206                     Os.close(fd);
    207                 }
    208             }
    209         } catch (IOException ex) {
    210             Log.e(TAG, "Zygote:  error closing sockets", ex);
    211         } catch (ErrnoException ex) {
    212             Log.e(TAG, "Zygote:  error closing descriptor", ex);
    213         }
    214 
    215         sServerSocket = null;
    216     }
    217 
    218     /**
    219      * Return the server socket's underlying file descriptor, so that
    220      * ZygoteConnection can pass it to the native code for proper
    221      * closure after a child process is forked off.
    222      */
    223 
    224     static FileDescriptor getServerSocketFileDescriptor() {
    225         return sServerSocket.getFileDescriptor();
    226     }
    227 
    228     private static final int UNPRIVILEGED_UID = 9999;
    229     private static final int UNPRIVILEGED_GID = 9999;
    230 
    231     private static final int ROOT_UID = 0;
    232     private static final int ROOT_GID = 0;
    233 
    234     /**
    235      * Sets effective user ID.
    236      */
    237     private static void setEffectiveUser(int uid) {
    238         int errno = setreuid(ROOT_UID, uid);
    239         if (errno != 0) {
    240             Log.e(TAG, "setreuid() failed. errno: " + errno);
    241         }
    242     }
    243 
    244     /**
    245      * Sets effective group ID.
    246      */
    247     private static void setEffectiveGroup(int gid) {
    248         int errno = setregid(ROOT_GID, gid);
    249         if (errno != 0) {
    250             Log.e(TAG, "setregid() failed. errno: " + errno);
    251         }
    252     }
    253 
    254     static void preload() {
    255         Log.d(TAG, "begin preload");
    256         preloadClasses();
    257         preloadResources();
    258         preloadOpenGL();
    259         preloadSharedLibraries();
    260         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
    261         // for memory sharing purposes.
    262         WebViewFactory.prepareWebViewInZygote();
    263         Log.d(TAG, "end preload");
    264     }
    265 
    266     private static void preloadSharedLibraries() {
    267         Log.i(TAG, "Preloading shared libraries...");
    268         System.loadLibrary("android");
    269         System.loadLibrary("compiler_rt");
    270         System.loadLibrary("jnigraphics");
    271     }
    272 
    273     private static void preloadOpenGL() {
    274         if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
    275             EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    276         }
    277     }
    278 
    279     /**
    280      * Performs Zygote process initialization. Loads and initializes
    281      * commonly used classes.
    282      *
    283      * Most classes only cause a few hundred bytes to be allocated, but
    284      * a few will allocate a dozen Kbytes (in one case, 500+K).
    285      */
    286     private static void preloadClasses() {
    287         final VMRuntime runtime = VMRuntime.getRuntime();
    288 
    289         InputStream is;
    290         try {
    291             is = new FileInputStream(PRELOADED_CLASSES);
    292         } catch (FileNotFoundException e) {
    293             Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
    294             return;
    295         }
    296 
    297         Log.i(TAG, "Preloading classes...");
    298         long startTime = SystemClock.uptimeMillis();
    299 
    300         // Drop root perms while running static initializers.
    301         setEffectiveGroup(UNPRIVILEGED_GID);
    302         setEffectiveUser(UNPRIVILEGED_UID);
    303 
    304         // Alter the target heap utilization.  With explicit GCs this
    305         // is not likely to have any effect.
    306         float defaultUtilization = runtime.getTargetHeapUtilization();
    307         runtime.setTargetHeapUtilization(0.8f);
    308 
    309         // Start with a clean slate.
    310         System.gc();
    311         runtime.runFinalizationSync();
    312         Debug.startAllocCounting();
    313 
    314         try {
    315             BufferedReader br
    316                 = new BufferedReader(new InputStreamReader(is), 256);
    317 
    318             int count = 0;
    319             String line;
    320             while ((line = br.readLine()) != null) {
    321                 // Skip comments and blank lines.
    322                 line = line.trim();
    323                 if (line.startsWith("#") || line.equals("")) {
    324                     continue;
    325                 }
    326 
    327                 try {
    328                     if (false) {
    329                         Log.v(TAG, "Preloading " + line + "...");
    330                     }
    331                     Class.forName(line);
    332                     if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
    333                         if (false) {
    334                             Log.v(TAG,
    335                                 " GC at " + Debug.getGlobalAllocSize());
    336                         }
    337                         System.gc();
    338                         runtime.runFinalizationSync();
    339                         Debug.resetGlobalAllocSize();
    340                     }
    341                     count++;
    342                 } catch (ClassNotFoundException e) {
    343                     Log.w(TAG, "Class not found for preloading: " + line);
    344                 } catch (UnsatisfiedLinkError e) {
    345                     Log.w(TAG, "Problem preloading " + line + ": " + e);
    346                 } catch (Throwable t) {
    347                     Log.e(TAG, "Error preloading " + line + ".", t);
    348                     if (t instanceof Error) {
    349                         throw (Error) t;
    350                     }
    351                     if (t instanceof RuntimeException) {
    352                         throw (RuntimeException) t;
    353                     }
    354                     throw new RuntimeException(t);
    355                 }
    356             }
    357 
    358             Log.i(TAG, "...preloaded " + count + " classes in "
    359                     + (SystemClock.uptimeMillis()-startTime) + "ms.");
    360         } catch (IOException e) {
    361             Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
    362         } finally {
    363             IoUtils.closeQuietly(is);
    364             // Restore default.
    365             runtime.setTargetHeapUtilization(defaultUtilization);
    366 
    367             // Fill in dex caches with classes, fields, and methods brought in by preloading.
    368             runtime.preloadDexCaches();
    369 
    370             Debug.stopAllocCounting();
    371 
    372             // Bring back root. We'll need it later.
    373             setEffectiveUser(ROOT_UID);
    374             setEffectiveGroup(ROOT_GID);
    375         }
    376     }
    377 
    378     /**
    379      * Load in commonly used resources, so they can be shared across
    380      * processes.
    381      *
    382      * These tend to be a few Kbytes, but are frequently in the 20-40K
    383      * range, and occasionally even larger.
    384      */
    385     private static void preloadResources() {
    386         final VMRuntime runtime = VMRuntime.getRuntime();
    387 
    388         Debug.startAllocCounting();
    389         try {
    390             System.gc();
    391             runtime.runFinalizationSync();
    392             mResources = Resources.getSystem();
    393             mResources.startPreloading();
    394             if (PRELOAD_RESOURCES) {
    395                 Log.i(TAG, "Preloading resources...");
    396 
    397                 long startTime = SystemClock.uptimeMillis();
    398                 TypedArray ar = mResources.obtainTypedArray(
    399                         com.android.internal.R.array.preloaded_drawables);
    400                 int N = preloadDrawables(runtime, ar);
    401                 ar.recycle();
    402                 Log.i(TAG, "...preloaded " + N + " resources in "
    403                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
    404 
    405                 startTime = SystemClock.uptimeMillis();
    406                 ar = mResources.obtainTypedArray(
    407                         com.android.internal.R.array.preloaded_color_state_lists);
    408                 N = preloadColorStateLists(runtime, ar);
    409                 ar.recycle();
    410                 Log.i(TAG, "...preloaded " + N + " resources in "
    411                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
    412             }
    413             mResources.finishPreloading();
    414         } catch (RuntimeException e) {
    415             Log.w(TAG, "Failure preloading resources", e);
    416         } finally {
    417             Debug.stopAllocCounting();
    418         }
    419     }
    420 
    421     private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
    422         int N = ar.length();
    423         for (int i=0; i<N; i++) {
    424             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
    425                 if (false) {
    426                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
    427                 }
    428                 System.gc();
    429                 runtime.runFinalizationSync();
    430                 Debug.resetGlobalAllocSize();
    431             }
    432             int id = ar.getResourceId(i, 0);
    433             if (false) {
    434                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
    435             }
    436             if (id != 0) {
    437                 if (mResources.getColorStateList(id) == null) {
    438                     throw new IllegalArgumentException(
    439                             "Unable to find preloaded color resource #0x"
    440                             + Integer.toHexString(id)
    441                             + " (" + ar.getString(i) + ")");
    442                 }
    443             }
    444         }
    445         return N;
    446     }
    447 
    448 
    449     private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
    450         int N = ar.length();
    451         for (int i=0; i<N; i++) {
    452             if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
    453                 if (false) {
    454                     Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
    455                 }
    456                 System.gc();
    457                 runtime.runFinalizationSync();
    458                 Debug.resetGlobalAllocSize();
    459             }
    460             int id = ar.getResourceId(i, 0);
    461             if (false) {
    462                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
    463             }
    464             if (id != 0) {
    465                 if (mResources.getDrawable(id, null) == null) {
    466                     throw new IllegalArgumentException(
    467                             "Unable to find preloaded drawable resource #0x"
    468                             + Integer.toHexString(id)
    469                             + " (" + ar.getString(i) + ")");
    470                 }
    471             }
    472         }
    473         return N;
    474     }
    475 
    476     /**
    477      * Runs several special GCs to try to clean up a few generations of
    478      * softly- and final-reachable objects, along with any other garbage.
    479      * This is only useful just before a fork().
    480      */
    481     /*package*/ static void gc() {
    482         final VMRuntime runtime = VMRuntime.getRuntime();
    483 
    484         /* runFinalizationSync() lets finalizers be called in Zygote,
    485          * which doesn't have a HeapWorker thread.
    486          */
    487         System.gc();
    488         runtime.runFinalizationSync();
    489         System.gc();
    490         runtime.runFinalizationSync();
    491         System.gc();
    492         runtime.runFinalizationSync();
    493     }
    494 
    495     /**
    496      * Finish remaining work for the newly forked system server process.
    497      */
    498     private static void handleSystemServerProcess(
    499             ZygoteConnection.Arguments parsedArgs)
    500             throws ZygoteInit.MethodAndArgsCaller {
    501 
    502         closeServerSocket();
    503 
    504         // set umask to 0077 so new files and directories will default to owner-only permissions.
    505         Os.umask(S_IRWXG | S_IRWXO);
    506 
    507         if (parsedArgs.niceName != null) {
    508             Process.setArgV0(parsedArgs.niceName);
    509         }
    510 
    511         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    512         if (systemServerClasspath != null) {
    513             performSystemServerDexOpt(systemServerClasspath);
    514         }
    515 
    516         if (parsedArgs.invokeWith != null) {
    517             String[] args = parsedArgs.remainingArgs;
    518             // If we have a non-null system server class path, we'll have to duplicate the
    519             // existing arguments and append the classpath to it. ART will handle the classpath
    520             // correctly when we exec a new process.
    521             if (systemServerClasspath != null) {
    522                 String[] amendedArgs = new String[args.length + 2];
    523                 amendedArgs[0] = "-cp";
    524                 amendedArgs[1] = systemServerClasspath;
    525                 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
    526             }
    527 
    528             WrapperInit.execApplication(parsedArgs.invokeWith,
    529                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
    530                     null, args);
    531         } else {
    532             ClassLoader cl = null;
    533             if (systemServerClasspath != null) {
    534                 cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
    535                 Thread.currentThread().setContextClassLoader(cl);
    536             }
    537 
    538             /*
    539              * Pass the remaining arguments to SystemServer.
    540              */
    541             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    542         }
    543 
    544         /* should never reach here */
    545     }
    546 
    547     /**
    548      * Performs dex-opt on the elements of {@code classPath}, if needed. We
    549      * choose the instruction set of the current runtime.
    550      */
    551     private static void performSystemServerDexOpt(String classPath) {
    552         final String[] classPathElements = classPath.split(":");
    553         final InstallerConnection installer = new InstallerConnection();
    554         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
    555 
    556         try {
    557             for (String classPathElement : classPathElements) {
    558                 final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
    559                         false /* defer */);
    560                 if (dexopt == DexFile.DEXOPT_NEEDED) {
    561                     installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
    562                 } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
    563                     installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
    564                 }
    565             }
    566         } catch (IOException ioe) {
    567             throw new RuntimeException("Error starting system_server", ioe);
    568         } finally {
    569             installer.disconnect();
    570         }
    571     }
    572 
    573     /**
    574      * Prepare the arguments and fork for the system server process.
    575      */
    576     private static boolean startSystemServer(String abiList, String socketName)
    577             throws MethodAndArgsCaller, RuntimeException {
    578         long capabilities = posixCapabilitiesAsBits(
    579             OsConstants.CAP_BLOCK_SUSPEND,
    580             OsConstants.CAP_KILL,
    581             OsConstants.CAP_NET_ADMIN,
    582             OsConstants.CAP_NET_BIND_SERVICE,
    583             OsConstants.CAP_NET_BROADCAST,
    584             OsConstants.CAP_NET_RAW,
    585             OsConstants.CAP_SYS_MODULE,
    586             OsConstants.CAP_SYS_NICE,
    587             OsConstants.CAP_SYS_RESOURCE,
    588             OsConstants.CAP_SYS_TIME,
    589             OsConstants.CAP_SYS_TTY_CONFIG
    590         );
    591         /* Hardcoded command line to start the system server */
    592         String args[] = {
    593             "--setuid=1000",
    594             "--setgid=1000",
    595             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
    596             "--capabilities=" + capabilities + "," + capabilities,
    597             "--runtime-init",
    598             "--nice-name=system_server",
    599             "com.android.server.SystemServer",
    600         };
    601         ZygoteConnection.Arguments parsedArgs = null;
    602 
    603         int pid;
    604 
    605         try {
    606             parsedArgs = new ZygoteConnection.Arguments(args);
    607             ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
    608             ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
    609 
    610             /* Request to fork the system server process */
    611             pid = Zygote.forkSystemServer(
    612                     parsedArgs.uid, parsedArgs.gid,
    613                     parsedArgs.gids,
    614                     parsedArgs.debugFlags,
    615                     null,
    616                     parsedArgs.permittedCapabilities,
    617                     parsedArgs.effectiveCapabilities);
    618         } catch (IllegalArgumentException ex) {
    619             throw new RuntimeException(ex);
    620         }
    621 
    622         /* For child process */
    623         if (pid == 0) {
    624             if (hasSecondZygote(abiList)) {
    625                 waitForSecondaryZygote(socketName);
    626             }
    627 
    628             handleSystemServerProcess(parsedArgs);
    629         }
    630 
    631         return true;
    632     }
    633 
    634     /**
    635      * Gets the bit array representation of the provided list of POSIX capabilities.
    636      */
    637     private static long posixCapabilitiesAsBits(int... capabilities) {
    638         long result = 0;
    639         for (int capability : capabilities) {
    640             if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
    641                 throw new IllegalArgumentException(String.valueOf(capability));
    642             }
    643             result |= (1L << capability);
    644         }
    645         return result;
    646     }
    647 
    648     public static void main(String argv[]) {
    649         try {
    650             // Start profiling the zygote initialization.
    651             SamplingProfilerIntegration.start();
    652 
    653             boolean startSystemServer = false;
    654             String socketName = "zygote";
    655             String abiList = null;
    656             for (int i = 1; i < argv.length; i++) {
    657                 if ("start-system-server".equals(argv[i])) {
    658                     startSystemServer = true;
    659                 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
    660                     abiList = argv[i].substring(ABI_LIST_ARG.length());
    661                 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
    662                     socketName = argv[i].substring(SOCKET_NAME_ARG.length());
    663                 } else {
    664                     throw new RuntimeException("Unknown command line argument: " + argv[i]);
    665                 }
    666             }
    667 
    668             if (abiList == null) {
    669                 throw new RuntimeException("No ABI list supplied.");
    670             }
    671 
    672             registerZygoteSocket(socketName);
    673             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
    674                 SystemClock.uptimeMillis());
    675             preload();
    676             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
    677                 SystemClock.uptimeMillis());
    678 
    679             // Finish profiling the zygote initialization.
    680             SamplingProfilerIntegration.writeZygoteSnapshot();
    681 
    682             // Do an initial gc to clean up after startup
    683             gc();
    684 
    685             // Disable tracing so that forked processes do not inherit stale tracing tags from
    686             // Zygote.
    687             Trace.setTracingEnabled(false);
    688 
    689             if (startSystemServer) {
    690                 startSystemServer(abiList, socketName);
    691             }
    692 
    693             Log.i(TAG, "Accepting command socket connections");
    694             runSelectLoop(abiList);
    695 
    696             closeServerSocket();
    697         } catch (MethodAndArgsCaller caller) {
    698             caller.run();
    699         } catch (RuntimeException ex) {
    700             Log.e(TAG, "Zygote died with exception", ex);
    701             closeServerSocket();
    702             throw ex;
    703         }
    704     }
    705 
    706     /**
    707      * Return {@code true} if this device configuration has another zygote.
    708      *
    709      * We determine this by comparing the device ABI list with this zygotes
    710      * list. If this zygote supports all ABIs this device supports, there won't
    711      * be another zygote.
    712      */
    713     private static boolean hasSecondZygote(String abiList) {
    714         return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
    715     }
    716 
    717     private static void waitForSecondaryZygote(String socketName) {
    718         String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
    719                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
    720         while (true) {
    721             try {
    722                 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
    723                 zs.close();
    724                 break;
    725             } catch (IOException ioe) {
    726                 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
    727             }
    728 
    729             try {
    730                 Thread.sleep(1000);
    731             } catch (InterruptedException ie) {
    732             }
    733         }
    734     }
    735 
    736     /**
    737      * Runs the zygote process's select loop. Accepts new connections as
    738      * they happen, and reads commands from connections one spawn-request's
    739      * worth at a time.
    740      *
    741      * @throws MethodAndArgsCaller in a child process when a main() should
    742      * be executed.
    743      */
    744     private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    745         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    746         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    747         FileDescriptor[] fdArray = new FileDescriptor[4];
    748 
    749         fds.add(sServerSocket.getFileDescriptor());
    750         peers.add(null);
    751 
    752         int loopCount = GC_LOOP_COUNT;
    753         while (true) {
    754             int index;
    755 
    756             /*
    757              * Call gc() before we block in select().
    758              * It's work that has to be done anyway, and it's better
    759              * to avoid making every child do it.  It will also
    760              * madvise() any free memory as a side-effect.
    761              *
    762              * Don't call it every time, because walking the entire
    763              * heap is a lot of overhead to free a few hundred bytes.
    764              */
    765             if (loopCount <= 0) {
    766                 gc();
    767                 loopCount = GC_LOOP_COUNT;
    768             } else {
    769                 loopCount--;
    770             }
    771 
    772 
    773             try {
    774                 fdArray = fds.toArray(fdArray);
    775                 index = selectReadable(fdArray);
    776             } catch (IOException ex) {
    777                 throw new RuntimeException("Error in select()", ex);
    778             }
    779 
    780             if (index < 0) {
    781                 throw new RuntimeException("Error in select()");
    782             } else if (index == 0) {
    783                 ZygoteConnection newPeer = acceptCommandPeer(abiList);
    784                 peers.add(newPeer);
    785                 fds.add(newPeer.getFileDescriptor());
    786             } else {
    787                 boolean done;
    788                 done = peers.get(index).runOnce();
    789 
    790                 if (done) {
    791                     peers.remove(index);
    792                     fds.remove(index);
    793                 }
    794             }
    795         }
    796     }
    797 
    798     /**
    799      * The Linux syscall "setreuid()"
    800      * @param ruid real uid
    801      * @param euid effective uid
    802      * @return 0 on success, non-zero errno on fail
    803      */
    804     static native int setreuid(int ruid, int euid);
    805 
    806     /**
    807      * The Linux syscall "setregid()"
    808      * @param rgid real gid
    809      * @param egid effective gid
    810      * @return 0 on success, non-zero errno on fail
    811      */
    812     static native int setregid(int rgid, int egid);
    813 
    814     /**
    815      * Invokes the linux syscall "setpgid"
    816      *
    817      * @param pid pid to change
    818      * @param pgid new process group of pid
    819      * @return 0 on success or non-zero errno on fail
    820      */
    821     static native int setpgid(int pid, int pgid);
    822 
    823     /**
    824      * Invokes the linux syscall "getpgid"
    825      *
    826      * @param pid pid to query
    827      * @return pgid of pid in question
    828      * @throws IOException on error
    829      */
    830     static native int getpgid(int pid) throws IOException;
    831 
    832     /**
    833      * Invokes the syscall dup2() to copy the specified descriptors into
    834      * stdin, stdout, and stderr. The existing stdio descriptors will be
    835      * closed and errors during close will be ignored. The specified
    836      * descriptors will also remain open at their original descriptor numbers,
    837      * so the caller may want to close the original descriptors.
    838      *
    839      * @param in new stdin
    840      * @param out new stdout
    841      * @param err new stderr
    842      * @throws IOException
    843      */
    844     static native void reopenStdio(FileDescriptor in,
    845             FileDescriptor out, FileDescriptor err) throws IOException;
    846 
    847     /**
    848      * Toggles the close-on-exec flag for the specified file descriptor.
    849      *
    850      * @param fd non-null; file descriptor
    851      * @param flag desired close-on-exec flag state
    852      * @throws IOException
    853      */
    854     static native void setCloseOnExec(FileDescriptor fd, boolean flag)
    855             throws IOException;
    856 
    857     /**
    858      * Invokes select() on the provider array of file descriptors (selecting
    859      * for readability only). Array elements of null are ignored.
    860      *
    861      * @param fds non-null; array of readable file descriptors
    862      * @return index of descriptor that is now readable or -1 for empty array.
    863      * @throws IOException if an error occurs
    864      */
    865     static native int selectReadable(FileDescriptor[] fds) throws IOException;
    866 
    867     /**
    868      * Creates a file descriptor from an int fd.
    869      *
    870      * @param fd integer OS file descriptor
    871      * @return non-null; FileDescriptor instance
    872      * @throws IOException if fd is invalid
    873      */
    874     static native FileDescriptor createFileDescriptor(int fd)
    875             throws IOException;
    876 
    877     /**
    878      * Class not instantiable.
    879      */
    880     private ZygoteInit() {
    881     }
    882 
    883     /**
    884      * Helper exception class which holds a method and arguments and
    885      * can call them. This is used as part of a trampoline to get rid of
    886      * the initial process setup stack frames.
    887      */
    888     public static class MethodAndArgsCaller extends Exception
    889             implements Runnable {
    890         /** method to call */
    891         private final Method mMethod;
    892 
    893         /** argument array */
    894         private final String[] mArgs;
    895 
    896         public MethodAndArgsCaller(Method method, String[] args) {
    897             mMethod = method;
    898             mArgs = args;
    899         }
    900 
    901         public void run() {
    902             try {
    903                 mMethod.invoke(null, new Object[] { mArgs });
    904             } catch (IllegalAccessException ex) {
    905                 throw new RuntimeException(ex);
    906             } catch (InvocationTargetException ex) {
    907                 Throwable cause = ex.getCause();
    908                 if (cause instanceof RuntimeException) {
    909                     throw (RuntimeException) cause;
    910                 } else if (cause instanceof Error) {
    911                     throw (Error) cause;
    912                 }
    913                 throw new RuntimeException(ex);
    914             }
    915         }
    916     }
    917 }
    918