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