blob: ac8b5893416845213ebd4b076322e4bee2e69181 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 android.content.pm.ActivityInfo;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.content.res.ColorStateList;
23import android.graphics.drawable.Drawable;
24import android.net.LocalServerSocket;
25import android.os.Debug;
26import android.os.SystemClock;
27import android.os.SystemProperties;
28import android.util.Config;
29import android.util.EventLog;
30import android.util.Log;
31
32import dalvik.system.VMRuntime;
33import dalvik.system.Zygote;
34
35import java.io.BufferedReader;
36import java.io.FileDescriptor;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.InputStreamReader;
40import java.lang.reflect.InvocationTargetException;
41import java.lang.reflect.Method;
42import java.lang.reflect.Modifier;
43import java.util.ArrayList;
44
45/**
46 * Startup class for the zygote process.
47 *
48 * Pre-initializes some classes, and then waits for commands on a UNIX domain
49 * socket. Based on these commands, forks of child processes that inherit
50 * the initial state of the VM.
51 *
52 * Please see {@link ZygoteConnection.Arguments} for documentation on the
53 * client protocol.
54 *
55 * @hide
56 */
57public class ZygoteInit {
58
59 private static final String TAG = "Zygote";
60
61 private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
62
63 private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
64 private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
65
66 /** when preloading, GC after allocating this many bytes */
67 private static final int PRELOAD_GC_THRESHOLD = 50000;
68
69 private static LocalServerSocket sServerSocket;
70
71 /**
72 * Used to pre-load resources. We hold a global reference on it so it
73 * never gets destroyed.
74 */
75 private static Resources mResources;
76
77 /**
78 * The number of times that the main Zygote loop
79 * should run before calling gc() again.
80 */
81 static final int GC_LOOP_COUNT = 10;
82
83 /**
84 * If true, zygote forks for each peer. If false, a select loop is used
85 * inside a single process. The latter is preferred.
86 */
87 private static final boolean ZYGOTE_FORK_MODE = false;
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 /**
227 * Performs Zygote process initialization. Loads and initializes
228 * commonly used classes.
229 *
230 * Most classes only cause a few hundred bytes to be allocated, but
231 * a few will allocate a dozen Kbytes (in one case, 500+K).
232 */
233 private static void preloadClasses() {
234 final VMRuntime runtime = VMRuntime.getRuntime();
235
236 InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
237 PRELOADED_CLASSES);
238 if (is == null) {
239 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
240 } else {
241 Log.i(TAG, "Preloading classes...");
242 long startTime = SystemClock.uptimeMillis();
243
244 // Drop root perms while running static initializers.
245 setEffectiveGroup(UNPRIVILEGED_GID);
246 setEffectiveUser(UNPRIVILEGED_UID);
247
248 // Alter the target heap utilization. With explicit GCs this
249 // is not likely to have any effect.
250 float defaultUtilization = runtime.getTargetHeapUtilization();
251 runtime.setTargetHeapUtilization(0.8f);
252
253 // Start with a clean slate.
254 runtime.gcSoftReferences();
255 runtime.runFinalizationSync();
256 Debug.startAllocCounting();
257
258 try {
259 BufferedReader br
260 = new BufferedReader(new InputStreamReader(is), 256);
261
262 int count = 0;
263 String line;
264 String missingClasses = null;
265 while ((line = br.readLine()) != null) {
266 // Skip comments and blank lines.
267 line = line.trim();
268 if (line.startsWith("#") || line.equals("")) {
269 continue;
270 }
271
272 try {
273 if (Config.LOGV) {
274 Log.v(TAG, "Preloading " + line + "...");
275 }
276 Class.forName(line);
277 if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
278 if (Config.LOGV) {
279 Log.v(TAG,
280 " GC at " + Debug.getGlobalAllocSize());
281 }
282 runtime.gcSoftReferences();
283 runtime.runFinalizationSync();
284 Debug.resetGlobalAllocSize();
285 }
286 count++;
287 } catch (ClassNotFoundException e) {
288 Log.e(TAG, "Class not found for preloading: " + line);
289 if (missingClasses == null) {
290 missingClasses = line;
291 } else {
292 missingClasses += " " + line;
293 }
294 }
295 }
296
297 if (missingClasses != null &&
298 "1".equals(SystemProperties.get("persist.service.adb.enable"))) {
299 throw new IllegalStateException(
300 "Missing class(es) for preloading, update preloaded-classes ["
301 + missingClasses + "]");
302 }
303
304 Log.i(TAG, "...preloaded " + count + " classes in "
305 + (SystemClock.uptimeMillis()-startTime) + "ms.");
306 } catch (IOException e) {
307 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
308 } finally {
309 // Restore default.
310 runtime.setTargetHeapUtilization(defaultUtilization);
311
312 Debug.stopAllocCounting();
313
314 // Bring back root. We'll need it later.
315 setEffectiveUser(ROOT_UID);
316 setEffectiveGroup(ROOT_GID);
317 }
318 }
319 }
320
321 /**
322 * Load in commonly used resources, so they can be shared across
323 * processes.
324 *
325 * These tend to be a few Kbytes, but are frequently in the 20-40K
326 * range, and occasionally even larger.
327 */
328 private static void preloadResources() {
329 final VMRuntime runtime = VMRuntime.getRuntime();
330
331 Debug.startAllocCounting();
332 try {
333 runtime.gcSoftReferences();
334 runtime.runFinalizationSync();
335 mResources = Resources.getSystem();
336 mResources.startPreloading();
337 if (PRELOAD_RESOURCES) {
338 Log.i(TAG, "Preloading resources...");
339
340 long startTime = SystemClock.uptimeMillis();
341 TypedArray ar = mResources.obtainTypedArray(
342 com.android.internal.R.array.preloaded_drawables);
343 int N = preloadDrawables(runtime, ar);
344 Log.i(TAG, "...preloaded " + N + " resources in "
345 + (SystemClock.uptimeMillis()-startTime) + "ms.");
346
347 startTime = SystemClock.uptimeMillis();
348 ar = mResources.obtainTypedArray(
349 com.android.internal.R.array.preloaded_color_state_lists);
350 N = preloadColorStateLists(runtime, ar);
351 Log.i(TAG, "...preloaded " + N + " resources in "
352 + (SystemClock.uptimeMillis()-startTime) + "ms.");
353 }
354 mResources.finishPreloading();
355 } catch (RuntimeException e) {
356 Log.w(TAG, "Failure preloading resources", e);
357 } finally {
358 Debug.stopAllocCounting();
359 }
360 }
361
362 private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
363 int N = ar.length();
364 for (int i=0; i<N; i++) {
365 if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
366 if (Config.LOGV) {
367 Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
368 }
369 runtime.gcSoftReferences();
370 runtime.runFinalizationSync();
371 Debug.resetGlobalAllocSize();
372 }
373 int id = ar.getResourceId(i, 0);
374 if (Config.LOGV) {
375 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
376 }
377 if (id != 0) {
378 mResources.getColorStateList(id);
379 }
380 }
381 return N;
382 }
383
384
385 private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
386 int N = ar.length();
387 for (int i=0; i<N; i++) {
388 if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
389 if (Config.LOGV) {
390 Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
391 }
392 runtime.gcSoftReferences();
393 runtime.runFinalizationSync();
394 Debug.resetGlobalAllocSize();
395 }
396 int id = ar.getResourceId(i, 0);
397 if (Config.LOGV) {
398 Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
399 }
400 if (id != 0) {
401 Drawable dr = mResources.getDrawable(id);
402 if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) {
403 Log.w(TAG, "Preloaded drawable resource #0x"
404 + Integer.toHexString(id)
405 + " (" + ar.getString(i) + ") that varies with configuration!!");
406 }
407 }
408 }
409 return N;
410 }
411
412 /**
413 * Runs several special GCs to try to clean up a few generations of
414 * softly- and final-reachable objects, along with any other garbage.
415 * This is only useful just before a fork().
416 */
417 /*package*/ static void gc() {
418 final VMRuntime runtime = VMRuntime.getRuntime();
419
420 /* runFinalizationSync() lets finalizers be called in Zygote,
421 * which doesn't have a HeapWorker thread.
422 */
423 runtime.gcSoftReferences();
424 runtime.runFinalizationSync();
425 runtime.gcSoftReferences();
426 runtime.runFinalizationSync();
427 runtime.gcSoftReferences();
428 runtime.runFinalizationSync();
429 }
430
431 /**
432 * Finish remaining work for the newly forked system server process.
433 */
434 private static void handleSystemServerProcess(
435 ZygoteConnection.Arguments parsedArgs)
436 throws ZygoteInit.MethodAndArgsCaller {
437 /*
438 * First, set the capabilities if necessary
439 */
440
441 if (parsedArgs.uid != 0) {
442 try {
443 setCapabilities(parsedArgs.permittedCapabilities,
444 parsedArgs.effectiveCapabilities);
445 } catch (IOException ex) {
446 Log.e(TAG, "Error setting capabilities", ex);
447 }
448 }
449
450 closeServerSocket();
451
452 /*
453 * Pass the remaining arguments to SystemServer.
454 * "--nice-name=system_server com.android.server.SystemServer"
455 */
456 RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
457 /* should never reach here */
458 }
459
460 /**
461 * Prepare the arguments and fork for the system server process.
462 */
463 private static boolean startSystemServer()
464 throws MethodAndArgsCaller, RuntimeException {
465 /* Hardcoded command line to start the system server */
466 String args[] = {
467 "--setuid=1000",
468 "--setgid=1000",
469 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
470 "--capabilities=121715744,121715744",
471 "--runtime-init",
472 "--nice-name=system_server",
473 "com.android.server.SystemServer",
474 };
475 ZygoteConnection.Arguments parsedArgs = null;
476
477 int pid;
478
479 try {
480 parsedArgs = new ZygoteConnection.Arguments(args);
481
482 /*
483 * Enable debugging of the system process if *either* the command line flags
484 * indicate it should be debuggable or the ro.debuggable system property
485 * is set to "1"
486 */
487 int debugFlags = parsedArgs.debugFlags;
488 if ("1".equals(SystemProperties.get("ro.debuggable")))
489 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
490
491 /* Request to fork the system server process */
492 pid = Zygote.forkSystemServer(
493 parsedArgs.uid, parsedArgs.gid,
494 parsedArgs.gids, debugFlags, null);
495 } catch (IllegalArgumentException ex) {
496 throw new RuntimeException(ex);
497 }
498
499 /* For child process */
500 if (pid == 0) {
501 handleSystemServerProcess(parsedArgs);
502 }
503
504 return true;
505 }
506
507 public static void main(String argv[]) {
508 try {
509 registerZygoteSocket();
510 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
511 SystemClock.uptimeMillis());
512 preloadClasses();
513 preloadResources();
514 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
515 SystemClock.uptimeMillis());
516
517 // Do an initial gc to clean up after startup
518 gc();
519
520 // If requested, start system server directly from Zygote
521 if (argv.length != 2) {
522 throw new RuntimeException(
523 "ZygoteInit.main expects two arguments");
524 }
525
526 if (argv[1].equals("true")) {
527 startSystemServer();
528 }
529
530 Log.i(TAG, "Accepting command socket connections");
531
532 if (ZYGOTE_FORK_MODE) {
533 runForkMode();
534 } else {
535 runSelectLoopMode();
536 }
537
538 closeServerSocket();
539 } catch (MethodAndArgsCaller caller) {
540 caller.run();
541 } catch (RuntimeException ex) {
542 Log.e(TAG, "Zygote died with exception", ex);
543 closeServerSocket();
544 throw ex;
545 }
546 }
547
548 /**
549 * Runs the zygote in accept-and-fork mode. In this mode, each peer
550 * gets its own zygote spawner process. This code is retained for
551 * reference only.
552 *
553 * @throws MethodAndArgsCaller in a child process when a main() should
554 * be executed.
555 */
556 private static void runForkMode() throws MethodAndArgsCaller {
557 while (true) {
558 ZygoteConnection peer = acceptCommandPeer();
559
560 int pid;
561
562 pid = Zygote.fork();
563
564 if (pid == 0) {
565 // The child process should handle the peer requests
566
567 // The child does not accept any more connections
568 try {
569 sServerSocket.close();
570 } catch (IOException ex) {
571 Log.e(TAG, "Zygote Child: error closing sockets", ex);
572 } finally {
573 sServerSocket = null;
574 }
575
576 peer.run();
577 break;
578 } else if (pid > 0) {
579 peer.closeSocket();
580 } else {
581 throw new RuntimeException("Error invoking fork()");
582 }
583 }
584 }
585
586 /**
587 * Runs the zygote process's select loop. Accepts new connections as
588 * they happen, and reads commands from connections one spawn-request's
589 * worth at a time.
590 *
591 * @throws MethodAndArgsCaller in a child process when a main() should
592 * be executed.
593 */
594 private static void runSelectLoopMode() throws MethodAndArgsCaller {
595 ArrayList<FileDescriptor> fds = new ArrayList();
596 ArrayList<ZygoteConnection> peers = new ArrayList();
597 FileDescriptor[] fdArray = new FileDescriptor[4];
598
599 fds.add(sServerSocket.getFileDescriptor());
600 peers.add(null);
601
602 int loopCount = GC_LOOP_COUNT;
603 while (true) {
604 int index;
605
606 /*
607 * Call gc() before we block in select().
608 * It's work that has to be done anyway, and it's better
609 * to avoid making every child do it. It will also
610 * madvise() any free memory as a side-effect.
611 *
612 * Don't call it every time, because walking the entire
613 * heap is a lot of overhead to free a few hundred bytes.
614 */
615 if (loopCount <= 0) {
616 gc();
617 loopCount = GC_LOOP_COUNT;
618 } else {
619 loopCount--;
620 }
621
622
623 try {
624 fdArray = fds.toArray(fdArray);
625 index = selectReadable(fdArray);
626 } catch (IOException ex) {
627 throw new RuntimeException("Error in select()", ex);
628 }
629
630 if (index < 0) {
631 throw new RuntimeException("Error in select()");
632 } else if (index == 0) {
633 ZygoteConnection newPeer = acceptCommandPeer();
634 peers.add(newPeer);
635 fds.add(newPeer.getFileDesciptor());
636 } else {
637 boolean done;
638 done = peers.get(index).runOnce();
639
640 if (done) {
641 peers.remove(index);
642 fds.remove(index);
643 }
644 }
645 }
646 }
647
648 /**
649 * The Linux syscall "setreuid()"
650 * @param ruid real uid
651 * @param euid effective uid
652 * @return 0 on success, non-zero errno on fail
653 */
654 static native int setreuid(int ruid, int euid);
655
656 /**
657 * The Linux syscall "setregid()"
658 * @param rgid real gid
659 * @param egid effective gid
660 * @return 0 on success, non-zero errno on fail
661 */
662 static native int setregid(int rgid, int egid);
663
664 /**
665 * Invokes the linux syscall "setpgid"
666 *
667 * @param pid pid to change
668 * @param pgid new process group of pid
669 * @return 0 on success or non-zero errno on fail
670 */
671 static native int setpgid(int pid, int pgid);
672
673 /**
674 * Invokes the linux syscall "getpgid"
675 *
676 * @param pid pid to query
677 * @return pgid of pid in question
678 * @throws IOException on error
679 */
680 static native int getpgid(int pid) throws IOException;
681
682 /**
683 * Invokes the syscall dup2() to copy the specified descriptors into
684 * stdin, stdout, and stderr. The existing stdio descriptors will be
685 * closed and errors during close will be ignored. The specified
686 * descriptors will also remain open at their original descriptor numbers,
687 * so the caller may want to close the original descriptors.
688 *
689 * @param in new stdin
690 * @param out new stdout
691 * @param err new stderr
692 * @throws IOException
693 */
694 static native void reopenStdio(FileDescriptor in,
695 FileDescriptor out, FileDescriptor err) throws IOException;
696
697 /**
698 * Calls close() on a file descriptor
699 *
700 * @param fd descriptor to close
701 * @throws IOException
702 */
703 static native void closeDescriptor(FileDescriptor fd)
704 throws IOException;
705
706 /**
707 * Toggles the close-on-exec flag for the specified file descriptor.
708 *
709 * @param fd non-null; file descriptor
710 * @param flag desired close-on-exec flag state
711 * @throws IOException
712 */
713 static native void setCloseOnExec(FileDescriptor fd, boolean flag)
714 throws IOException;
715
716 /**
717 * Retrieves the permitted capability set from another process.
718 *
719 * @param pid &gt;=0 process ID or 0 for this process
720 * @throws IOException on error
721 */
722 static native long capgetPermitted(int pid)
723 throws IOException;
724
725 /**
726 * Sets the permitted and effective capability sets of this process.
727 *
728 * @param permittedCapabilities permitted set
729 * @param effectiveCapabilities effective set
730 * @throws IOException on error
731 */
732 static native void setCapabilities(
733 long permittedCapabilities,
734 long effectiveCapabilities) throws IOException;
735
736 /**
737 * Invokes select() on the provider array of file descriptors (selecting
738 * for readability only). Array elements of null are ignored.
739 *
740 * @param fds non-null; array of readable file descriptors
741 * @return index of descriptor that is now readable or -1 for empty array.
742 * @throws IOException if an error occurs
743 */
744 static native int selectReadable(FileDescriptor[] fds) throws IOException;
745
746 /**
747 * Creates a file descriptor from an int fd.
748 *
749 * @param fd integer OS file descriptor
750 * @return non-null; FileDescriptor instance
751 * @throws IOException if fd is invalid
752 */
753 static native FileDescriptor createFileDescriptor(int fd)
754 throws IOException;
755
756 /**
757 * Class not instantiable.
758 */
759 private ZygoteInit() {
760 }
761
762 /**
763 * Helper exception class which holds a method and arguments and
764 * can call them. This is used as part of a trampoline to get rid of
765 * the initial process setup stack frames.
766 */
767 public static class MethodAndArgsCaller extends Exception
768 implements Runnable {
769 /** method to call */
770 private final Method mMethod;
771
772 /** argument array */
773 private final String[] mArgs;
774
775 public MethodAndArgsCaller(Method method, String[] args) {
776 mMethod = method;
777 mArgs = args;
778 }
779
780 public void run() {
781 try {
782 mMethod.invoke(null, new Object[] { mArgs });
783 } catch (IllegalAccessException ex) {
784 throw new RuntimeException(ex);
785 } catch (InvocationTargetException ex) {
786 Throwable cause = ex.getCause();
787 if (cause instanceof RuntimeException) {
788 throw (RuntimeException) cause;
789 } else if (cause instanceof Error) {
790 throw (Error) cause;
791 }
792 throw new RuntimeException(ex);
793 }
794 }
795 }
796}