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