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.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.net.LocalServerSocket;
     26 import android.opengl.EGL14;
     27 import android.os.Process;
     28 import android.os.SystemClock;
     29 import android.os.SystemProperties;
     30 import android.os.Trace;
     31 import android.system.ErrnoException;
     32 import android.system.Os;
     33 import android.system.OsConstants;
     34 import android.system.StructPollfd;
     35 import android.text.Hyphenator;
     36 import android.util.EventLog;
     37 import android.util.Log;
     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.util.ArrayList;
     56 
     57 /**
     58  * Startup class for the zygote process.
     59  *
     60  * Pre-initializes some classes, and then waits for commands on a UNIX domain
     61  * socket. Based on these commands, forks off child processes that inherit
     62  * the initial state of the VM.
     63  *
     64  * Please see {@link ZygoteConnection.Arguments} for documentation on the
     65  * client protocol.
     66  *
     67  * @hide
     68  */
     69 public class ZygoteInit {
     70     private static final String TAG = "Zygote";
     71 
     72     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
     73 
     74     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
     75 
     76     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
     77     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
     78 
     79     /** when preloading, GC after allocating this many bytes */
     80     private static final int PRELOAD_GC_THRESHOLD = 50000;
     81 
     82     private static final String ABI_LIST_ARG = "--abi-list=";
     83 
     84     private static final String SOCKET_NAME_ARG = "--socket-name=";
     85 
     86     private static LocalServerSocket sServerSocket;
     87 
     88     /**
     89      * Used to pre-load resources.  We hold a global reference on it so it
     90      * never gets destroyed.
     91      */
     92     private static Resources mResources;
     93 
     94     /**
     95      * The path of a file that contains classes to preload.
     96      */
     97     private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
     98 
     99     /** Controls whether we should preload resources during zygote init. */
    100     private static final boolean PRELOAD_RESOURCES = true;
    101 
    102     /**
    103      * Registers a server socket for zygote command connections
    104      *
    105      * @throws RuntimeException when open fails
    106      */
    107     private static void registerZygoteSocket(String socketName) {
    108         if (sServerSocket == null) {
    109             int fileDesc;
    110             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
    111             try {
    112                 String env = System.getenv(fullSocketName);
    113                 fileDesc = Integer.parseInt(env);
    114             } catch (RuntimeException ex) {
    115                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
    116             }
    117 
    118             try {
    119                 FileDescriptor fd = new FileDescriptor();
    120                 fd.setInt$(fileDesc);
    121                 sServerSocket = new LocalServerSocket(fd);
    122             } catch (IOException ex) {
    123                 throw new RuntimeException(
    124                         "Error binding to local socket '" + fileDesc + "'", ex);
    125             }
    126         }
    127     }
    128 
    129     /**
    130      * Waits for and accepts a single command connection. Throws
    131      * RuntimeException on failure.
    132      */
    133     private static ZygoteConnection acceptCommandPeer(String abiList) {
    134         try {
    135             return new ZygoteConnection(sServerSocket.accept(), abiList);
    136         } catch (IOException ex) {
    137             throw new RuntimeException(
    138                     "IOException during accept()", ex);
    139         }
    140     }
    141 
    142     /**
    143      * Close and clean up zygote sockets. Called on shutdown and on the
    144      * child's exit path.
    145      */
    146     static void closeServerSocket() {
    147         try {
    148             if (sServerSocket != null) {
    149                 FileDescriptor fd = sServerSocket.getFileDescriptor();
    150                 sServerSocket.close();
    151                 if (fd != null) {
    152                     Os.close(fd);
    153                 }
    154             }
    155         } catch (IOException ex) {
    156             Log.e(TAG, "Zygote:  error closing sockets", ex);
    157         } catch (ErrnoException ex) {
    158             Log.e(TAG, "Zygote:  error closing descriptor", ex);
    159         }
    160 
    161         sServerSocket = null;
    162     }
    163 
    164     /**
    165      * Return the server socket's underlying file descriptor, so that
    166      * ZygoteConnection can pass it to the native code for proper
    167      * closure after a child process is forked off.
    168      */
    169 
    170     static FileDescriptor getServerSocketFileDescriptor() {
    171         return sServerSocket.getFileDescriptor();
    172     }
    173 
    174     private static final int UNPRIVILEGED_UID = 9999;
    175     private static final int UNPRIVILEGED_GID = 9999;
    176 
    177     private static final int ROOT_UID = 0;
    178     private static final int ROOT_GID = 0;
    179 
    180     static void preload() {
    181         Log.d(TAG, "begin preload");
    182         preloadClasses();
    183         preloadResources();
    184         preloadOpenGL();
    185         preloadSharedLibraries();
    186         preloadTextResources();
    187         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
    188         // for memory sharing purposes.
    189         WebViewFactory.prepareWebViewInZygote();
    190         Log.d(TAG, "end preload");
    191     }
    192 
    193     private static void preloadSharedLibraries() {
    194         Log.i(TAG, "Preloading shared libraries...");
    195         System.loadLibrary("android");
    196         System.loadLibrary("compiler_rt");
    197         System.loadLibrary("jnigraphics");
    198     }
    199 
    200     private static void preloadOpenGL() {
    201         if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
    202             EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    203         }
    204     }
    205 
    206     private static void preloadTextResources() {
    207         Hyphenator.init();
    208     }
    209 
    210     /**
    211      * Performs Zygote process initialization. Loads and initializes
    212      * commonly used classes.
    213      *
    214      * Most classes only cause a few hundred bytes to be allocated, but
    215      * a few will allocate a dozen Kbytes (in one case, 500+K).
    216      */
    217     private static void preloadClasses() {
    218         final VMRuntime runtime = VMRuntime.getRuntime();
    219 
    220         InputStream is;
    221         try {
    222             is = new FileInputStream(PRELOADED_CLASSES);
    223         } catch (FileNotFoundException e) {
    224             Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
    225             return;
    226         }
    227 
    228         Log.i(TAG, "Preloading classes...");
    229         long startTime = SystemClock.uptimeMillis();
    230 
    231         // Drop root perms while running static initializers.
    232         final int reuid = Os.getuid();
    233         final int regid = Os.getgid();
    234 
    235         // We need to drop root perms only if we're already root. In the case of "wrapped"
    236         // processes (see WrapperInit), this function is called from an unprivileged uid
    237         // and gid.
    238         boolean droppedPriviliges = false;
    239         if (reuid == ROOT_UID && regid == ROOT_GID) {
    240             try {
    241                 Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
    242                 Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
    243             } catch (ErrnoException ex) {
    244                 throw new RuntimeException("Failed to drop root", ex);
    245             }
    246 
    247             droppedPriviliges = true;
    248         }
    249 
    250         // Alter the target heap utilization.  With explicit GCs this
    251         // is not likely to have any effect.
    252         float defaultUtilization = runtime.getTargetHeapUtilization();
    253         runtime.setTargetHeapUtilization(0.8f);
    254 
    255         try {
    256             BufferedReader br
    257                 = new BufferedReader(new InputStreamReader(is), 256);
    258 
    259             int count = 0;
    260             String line;
    261             while ((line = br.readLine()) != null) {
    262                 // Skip comments and blank lines.
    263                 line = line.trim();
    264                 if (line.startsWith("#") || line.equals("")) {
    265                     continue;
    266                 }
    267 
    268                 try {
    269                     if (false) {
    270                         Log.v(TAG, "Preloading " + line + "...");
    271                     }
    272                     // Load and explicitly initialize the given class. Use
    273                     // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
    274                     // (to derive the caller's class-loader). Use true to force initialization, and
    275                     // null for the boot classpath class-loader (could as well cache the
    276                     // class-loader of this class in a variable).
    277                     Class.forName(line, true, null);
    278                     count++;
    279                 } catch (ClassNotFoundException e) {
    280                     Log.w(TAG, "Class not found for preloading: " + line);
    281                 } catch (UnsatisfiedLinkError e) {
    282                     Log.w(TAG, "Problem preloading " + line + ": " + e);
    283                 } catch (Throwable t) {
    284                     Log.e(TAG, "Error preloading " + line + ".", t);
    285                     if (t instanceof Error) {
    286                         throw (Error) t;
    287                     }
    288                     if (t instanceof RuntimeException) {
    289                         throw (RuntimeException) t;
    290                     }
    291                     throw new RuntimeException(t);
    292                 }
    293             }
    294 
    295             Log.i(TAG, "...preloaded " + count + " classes in "
    296                     + (SystemClock.uptimeMillis()-startTime) + "ms.");
    297         } catch (IOException e) {
    298             Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
    299         } finally {
    300             IoUtils.closeQuietly(is);
    301             // Restore default.
    302             runtime.setTargetHeapUtilization(defaultUtilization);
    303 
    304             // Fill in dex caches with classes, fields, and methods brought in by preloading.
    305             runtime.preloadDexCaches();
    306 
    307             // Bring back root. We'll need it later if we're in the zygote.
    308             if (droppedPriviliges) {
    309                 try {
    310                     Os.setreuid(ROOT_UID, ROOT_UID);
    311                     Os.setregid(ROOT_GID, ROOT_GID);
    312                 } catch (ErrnoException ex) {
    313                     throw new RuntimeException("Failed to restore root", ex);
    314                 }
    315             }
    316         }
    317     }
    318 
    319     /**
    320      * Load in commonly used resources, so they can be shared across
    321      * processes.
    322      *
    323      * These tend to be a few Kbytes, but are frequently in the 20-40K
    324      * range, and occasionally even larger.
    325      */
    326     private static void preloadResources() {
    327         final VMRuntime runtime = VMRuntime.getRuntime();
    328 
    329         try {
    330             mResources = Resources.getSystem();
    331             mResources.startPreloading();
    332             if (PRELOAD_RESOURCES) {
    333                 Log.i(TAG, "Preloading resources...");
    334 
    335                 long startTime = SystemClock.uptimeMillis();
    336                 TypedArray ar = mResources.obtainTypedArray(
    337                         com.android.internal.R.array.preloaded_drawables);
    338                 int N = preloadDrawables(runtime, ar);
    339                 ar.recycle();
    340                 Log.i(TAG, "...preloaded " + N + " resources in "
    341                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
    342 
    343                 startTime = SystemClock.uptimeMillis();
    344                 ar = mResources.obtainTypedArray(
    345                         com.android.internal.R.array.preloaded_color_state_lists);
    346                 N = preloadColorStateLists(runtime, ar);
    347                 ar.recycle();
    348                 Log.i(TAG, "...preloaded " + N + " resources in "
    349                         + (SystemClock.uptimeMillis()-startTime) + "ms.");
    350             }
    351             mResources.finishPreloading();
    352         } catch (RuntimeException e) {
    353             Log.w(TAG, "Failure preloading resources", e);
    354         }
    355     }
    356 
    357     private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
    358         int N = ar.length();
    359         for (int i=0; i<N; i++) {
    360             int id = ar.getResourceId(i, 0);
    361             if (false) {
    362                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
    363             }
    364             if (id != 0) {
    365                 if (mResources.getColorStateList(id, null) == null) {
    366                     throw new IllegalArgumentException(
    367                             "Unable to find preloaded color resource #0x"
    368                             + Integer.toHexString(id)
    369                             + " (" + ar.getString(i) + ")");
    370                 }
    371             }
    372         }
    373         return N;
    374     }
    375 
    376 
    377     private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
    378         int N = ar.length();
    379         for (int i=0; i<N; i++) {
    380             int id = ar.getResourceId(i, 0);
    381             if (false) {
    382                 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
    383             }
    384             if (id != 0) {
    385                 if (mResources.getDrawable(id, null) == null) {
    386                     throw new IllegalArgumentException(
    387                             "Unable to find preloaded drawable resource #0x"
    388                             + Integer.toHexString(id)
    389                             + " (" + ar.getString(i) + ")");
    390                 }
    391             }
    392         }
    393         return N;
    394     }
    395 
    396     /**
    397      * Runs several special GCs to try to clean up a few generations of
    398      * softly- and final-reachable objects, along with any other garbage.
    399      * This is only useful just before a fork().
    400      */
    401     /*package*/ static void gcAndFinalize() {
    402         final VMRuntime runtime = VMRuntime.getRuntime();
    403 
    404         /* runFinalizationSync() lets finalizers be called in Zygote,
    405          * which doesn't have a HeapWorker thread.
    406          */
    407         System.gc();
    408         runtime.runFinalizationSync();
    409         System.gc();
    410     }
    411 
    412     /**
    413      * Finish remaining work for the newly forked system server process.
    414      */
    415     private static void handleSystemServerProcess(
    416             ZygoteConnection.Arguments parsedArgs)
    417             throws ZygoteInit.MethodAndArgsCaller {
    418 
    419         closeServerSocket();
    420 
    421         // set umask to 0077 so new files and directories will default to owner-only permissions.
    422         Os.umask(S_IRWXG | S_IRWXO);
    423 
    424         if (parsedArgs.niceName != null) {
    425             Process.setArgV0(parsedArgs.niceName);
    426         }
    427 
    428         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    429         if (systemServerClasspath != null) {
    430             performSystemServerDexOpt(systemServerClasspath);
    431         }
    432 
    433         if (parsedArgs.invokeWith != null) {
    434             String[] args = parsedArgs.remainingArgs;
    435             // If we have a non-null system server class path, we'll have to duplicate the
    436             // existing arguments and append the classpath to it. ART will handle the classpath
    437             // correctly when we exec a new process.
    438             if (systemServerClasspath != null) {
    439                 String[] amendedArgs = new String[args.length + 2];
    440                 amendedArgs[0] = "-cp";
    441                 amendedArgs[1] = systemServerClasspath;
    442                 System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
    443             }
    444 
    445             WrapperInit.execApplication(parsedArgs.invokeWith,
    446                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
    447                     VMRuntime.getCurrentInstructionSet(), null, args);
    448         } else {
    449             ClassLoader cl = null;
    450             if (systemServerClasspath != null) {
    451                 cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
    452                 Thread.currentThread().setContextClassLoader(cl);
    453             }
    454 
    455             /*
    456              * Pass the remaining arguments to SystemServer.
    457              */
    458             RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    459         }
    460 
    461         /* should never reach here */
    462     }
    463 
    464     /**
    465      * Performs dex-opt on the elements of {@code classPath}, if needed. We
    466      * choose the instruction set of the current runtime.
    467      */
    468     private static void performSystemServerDexOpt(String classPath) {
    469         final String[] classPathElements = classPath.split(":");
    470         final InstallerConnection installer = new InstallerConnection();
    471         installer.waitForConnection();
    472         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
    473 
    474         try {
    475             for (String classPathElement : classPathElements) {
    476                 final int dexoptNeeded = DexFile.getDexOptNeeded(
    477                         classPathElement, "*", instructionSet, false /* defer */);
    478                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
    479                     installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
    480                             instructionSet, dexoptNeeded);
    481                 }
    482             }
    483         } catch (IOException ioe) {
    484             throw new RuntimeException("Error starting system_server", ioe);
    485         } finally {
    486             installer.disconnect();
    487         }
    488     }
    489 
    490     /**
    491      * Prepare the arguments and fork for the system server process.
    492      */
    493     private static boolean startSystemServer(String abiList, String socketName)
    494             throws MethodAndArgsCaller, RuntimeException {
    495         long capabilities = posixCapabilitiesAsBits(
    496             OsConstants.CAP_BLOCK_SUSPEND,
    497             OsConstants.CAP_KILL,
    498             OsConstants.CAP_NET_ADMIN,
    499             OsConstants.CAP_NET_BIND_SERVICE,
    500             OsConstants.CAP_NET_BROADCAST,
    501             OsConstants.CAP_NET_RAW,
    502             OsConstants.CAP_SYS_MODULE,
    503             OsConstants.CAP_SYS_NICE,
    504             OsConstants.CAP_SYS_RESOURCE,
    505             OsConstants.CAP_SYS_TIME,
    506             OsConstants.CAP_SYS_TTY_CONFIG
    507         );
    508         /* Hardcoded command line to start the system server */
    509         String args[] = {
    510             "--setuid=1000",
    511             "--setgid=1000",
    512             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
    513             "--capabilities=" + capabilities + "," + capabilities,
    514             "--nice-name=system_server",
    515             "--runtime-args",
    516             "com.android.server.SystemServer",
    517         };
    518         ZygoteConnection.Arguments parsedArgs = null;
    519 
    520         int pid;
    521 
    522         try {
    523             parsedArgs = new ZygoteConnection.Arguments(args);
    524             ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
    525             ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
    526 
    527             /* Request to fork the system server process */
    528             pid = Zygote.forkSystemServer(
    529                     parsedArgs.uid, parsedArgs.gid,
    530                     parsedArgs.gids,
    531                     parsedArgs.debugFlags,
    532                     null,
    533                     parsedArgs.permittedCapabilities,
    534                     parsedArgs.effectiveCapabilities);
    535         } catch (IllegalArgumentException ex) {
    536             throw new RuntimeException(ex);
    537         }
    538 
    539         /* For child process */
    540         if (pid == 0) {
    541             if (hasSecondZygote(abiList)) {
    542                 waitForSecondaryZygote(socketName);
    543             }
    544 
    545             handleSystemServerProcess(parsedArgs);
    546         }
    547 
    548         return true;
    549     }
    550 
    551     /**
    552      * Gets the bit array representation of the provided list of POSIX capabilities.
    553      */
    554     private static long posixCapabilitiesAsBits(int... capabilities) {
    555         long result = 0;
    556         for (int capability : capabilities) {
    557             if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
    558                 throw new IllegalArgumentException(String.valueOf(capability));
    559             }
    560             result |= (1L << capability);
    561         }
    562         return result;
    563     }
    564 
    565     public static void main(String argv[]) {
    566         try {
    567             RuntimeInit.enableDdms();
    568             // Start profiling the zygote initialization.
    569             SamplingProfilerIntegration.start();
    570 
    571             boolean startSystemServer = false;
    572             String socketName = "zygote";
    573             String abiList = null;
    574             for (int i = 1; i < argv.length; i++) {
    575                 if ("start-system-server".equals(argv[i])) {
    576                     startSystemServer = true;
    577                 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
    578                     abiList = argv[i].substring(ABI_LIST_ARG.length());
    579                 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
    580                     socketName = argv[i].substring(SOCKET_NAME_ARG.length());
    581                 } else {
    582                     throw new RuntimeException("Unknown command line argument: " + argv[i]);
    583                 }
    584             }
    585 
    586             if (abiList == null) {
    587                 throw new RuntimeException("No ABI list supplied.");
    588             }
    589 
    590             registerZygoteSocket(socketName);
    591             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
    592                 SystemClock.uptimeMillis());
    593             preload();
    594             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
    595                 SystemClock.uptimeMillis());
    596 
    597             // Finish profiling the zygote initialization.
    598             SamplingProfilerIntegration.writeZygoteSnapshot();
    599 
    600             // Do an initial gc to clean up after startup
    601             gcAndFinalize();
    602 
    603             // Disable tracing so that forked processes do not inherit stale tracing tags from
    604             // Zygote.
    605             Trace.setTracingEnabled(false);
    606 
    607             if (startSystemServer) {
    608                 startSystemServer(abiList, socketName);
    609             }
    610 
    611             Log.i(TAG, "Accepting command socket connections");
    612             runSelectLoop(abiList);
    613 
    614             closeServerSocket();
    615         } catch (MethodAndArgsCaller caller) {
    616             caller.run();
    617         } catch (RuntimeException ex) {
    618             Log.e(TAG, "Zygote died with exception", ex);
    619             closeServerSocket();
    620             throw ex;
    621         }
    622     }
    623 
    624     /**
    625      * Return {@code true} if this device configuration has another zygote.
    626      *
    627      * We determine this by comparing the device ABI list with this zygotes
    628      * list. If this zygote supports all ABIs this device supports, there won't
    629      * be another zygote.
    630      */
    631     private static boolean hasSecondZygote(String abiList) {
    632         return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
    633     }
    634 
    635     private static void waitForSecondaryZygote(String socketName) {
    636         String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
    637                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
    638         while (true) {
    639             try {
    640                 final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
    641                 zs.close();
    642                 break;
    643             } catch (IOException ioe) {
    644                 Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
    645             }
    646 
    647             try {
    648                 Thread.sleep(1000);
    649             } catch (InterruptedException ie) {
    650             }
    651         }
    652     }
    653 
    654     /**
    655      * Runs the zygote process's select loop. Accepts new connections as
    656      * they happen, and reads commands from connections one spawn-request's
    657      * worth at a time.
    658      *
    659      * @throws MethodAndArgsCaller in a child process when a main() should
    660      * be executed.
    661      */
    662     private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    663         ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    664         ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    665 
    666         fds.add(sServerSocket.getFileDescriptor());
    667         peers.add(null);
    668 
    669         while (true) {
    670             StructPollfd[] pollFds = new StructPollfd[fds.size()];
    671             for (int i = 0; i < pollFds.length; ++i) {
    672                 pollFds[i] = new StructPollfd();
    673                 pollFds[i].fd = fds.get(i);
    674                 pollFds[i].events = (short) POLLIN;
    675             }
    676             try {
    677                 Os.poll(pollFds, -1);
    678             } catch (ErrnoException ex) {
    679                 throw new RuntimeException("poll failed", ex);
    680             }
    681             for (int i = pollFds.length - 1; i >= 0; --i) {
    682                 if ((pollFds[i].revents & POLLIN) == 0) {
    683                     continue;
    684                 }
    685                 if (i == 0) {
    686                     ZygoteConnection newPeer = acceptCommandPeer(abiList);
    687                     peers.add(newPeer);
    688                     fds.add(newPeer.getFileDesciptor());
    689                 } else {
    690                     boolean done = peers.get(i).runOnce();
    691                     if (done) {
    692                         peers.remove(i);
    693                         fds.remove(i);
    694                     }
    695                 }
    696             }
    697         }
    698     }
    699 
    700     /**
    701      * Class not instantiable.
    702      */
    703     private ZygoteInit() {
    704     }
    705 
    706     /**
    707      * Helper exception class which holds a method and arguments and
    708      * can call them. This is used as part of a trampoline to get rid of
    709      * the initial process setup stack frames.
    710      */
    711     public static class MethodAndArgsCaller extends Exception
    712             implements Runnable {
    713         /** method to call */
    714         private final Method mMethod;
    715 
    716         /** argument array */
    717         private final String[] mArgs;
    718 
    719         public MethodAndArgsCaller(Method method, String[] args) {
    720             mMethod = method;
    721             mArgs = args;
    722         }
    723 
    724         public void run() {
    725             try {
    726                 mMethod.invoke(null, new Object[] { mArgs });
    727             } catch (IllegalAccessException ex) {
    728                 throw new RuntimeException(ex);
    729             } catch (InvocationTargetException ex) {
    730                 Throwable cause = ex.getCause();
    731                 if (cause instanceof RuntimeException) {
    732                     throw (RuntimeException) cause;
    733                 } else if (cause instanceof Error) {
    734                     throw (Error) cause;
    735                 }
    736                 throw new RuntimeException(ex);
    737             }
    738         }
    739     }
    740 }
    741