blob: c851e4ec959a511ed9b1b4a148c10c860c0c7729 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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.app.ActivityManagerNative;
Eric Rowe35980b22013-09-03 10:51:15 -070020import android.app.ActivityThread;
Dan Egnorb7f03672009-12-09 16:22:32 -080021import android.app.ApplicationErrorReport;
Dan Egnorb7f03672009-12-09 16:22:32 -080022import android.os.Build;
Jeff Sharkeyf8880562016-02-26 13:03:01 -070023import android.os.DeadObjectException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.os.Debug;
25import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.SystemProperties;
Narayan Kamathfbb32f62015-06-12 15:34:35 +010028import android.os.Trace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.util.Log;
Dianne Hackbornc9421ba2010-03-11 22:23:46 -080030import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import com.android.internal.logging.AndroidConfig;
Jesse Wilson8568db52011-06-28 19:06:31 -070032import com.android.server.NetworkManagementSocketTagger;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import dalvik.system.VMRuntime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import java.lang.reflect.Method;
35import java.lang.reflect.Modifier;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import java.util.TimeZone;
Jesse Wilson8568db52011-06-28 19:06:31 -070037import java.util.logging.LogManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import org.apache.harmony.luni.internal.util.TimezoneGetter;
39
40/**
41 * Main entry point for runtime initialization. Not for
42 * public consumption.
43 * @hide
44 */
45public class RuntimeInit {
46 private final static String TAG = "AndroidRuntime";
Jeff Brownebed7d62011-05-16 17:08:42 -070047 private final static boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49 /** true if commonInit() has been called */
50 private static boolean initialized;
51
Dan Egnor60d87622009-12-16 16:32:58 -080052 private static IBinder mApplicationObject;
53
54 private static volatile boolean mCrashing = false;
55
Jeff Brown16f5f5c2012-03-15 16:53:55 -070056 private static final native void nativeZygoteInit();
57 private static final native void nativeFinishInit();
Jeff Brown4280c4a2012-03-15 17:48:02 -070058 private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
Jeff Brown16f5f5c2012-03-15 16:53:55 -070059
Mark Salyzyn69eb6f52014-04-09 07:39:15 -070060 private static int Clog_e(String tag, String msg, Throwable tr) {
Andreas Gamped888beb2016-02-18 14:01:41 -080061 return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
Mark Salyzyn69eb6f52014-04-09 07:39:15 -070062 }
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 /**
Tobias Thiereraddbf902016-07-21 15:05:19 +010065 * Logs a message when a thread encounters an uncaught exception. By
66 * default, {@link KillApplicationHandler} will terminate this process later,
67 * but apps can override that behavior.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 */
Tobias Thiereraddbf902016-07-21 15:05:19 +010069 private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
70 @Override
71 public void uncaughtException(Thread t, Throwable e) {
72 // Don't re-enter if KillApplicationHandler has already run
73 if (mCrashing) return;
74 if (mApplicationObject == null) {
75 // The "FATAL EXCEPTION" string is still used on Android even though
76 // apps can set a custom UncaughtExceptionHandler that renders uncaught
77 // exceptions non-fatal.
78 Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
79 } else {
80 StringBuilder message = new StringBuilder();
81 // The "FATAL EXCEPTION" string is still used on Android even though
82 // apps can set a custom UncaughtExceptionHandler that renders uncaught
83 // exceptions non-fatal.
84 message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
85 final String processName = ActivityThread.currentProcessName();
86 if (processName != null) {
87 message.append("Process: ").append(processName).append(", ");
88 }
89 message.append("PID: ").append(Process.myPid());
90 Clog_e(TAG, message.toString(), e);
91 }
92 }
93 }
94
95 /**
96 * Handle application death from an uncaught exception. The framework
97 * catches these for the main threads, so this should only matter for
98 * threads created by applications. Before this method runs,
99 * {@link LoggingHandler} will already have logged details.
100 */
101 private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 public void uncaughtException(Thread t, Throwable e) {
103 try {
Dan Egnor60d87622009-12-16 16:32:58 -0800104 // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
105 if (mCrashing) return;
106 mCrashing = true;
107
Andreas Gampe4c79fea2016-01-28 20:11:41 -0800108 // Try to end profiling. If a profiler is running at this point, and we kill the
109 // process (below), the in-memory buffer will be lost. So try to stop, which will
110 // flush the buffer. (This makes method trace profiling useful to debug crashes.)
111 if (ActivityThread.currentActivityThread() != null) {
112 ActivityThread.currentActivityThread().stopProfiling();
113 }
114
Dan Egnor60d87622009-12-16 16:32:58 -0800115 // Bring up crash dialog, wait for it to be dismissed
116 ActivityManagerNative.getDefault().handleApplicationCrash(
117 mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
118 } catch (Throwable t2) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700119 if (t2 instanceof DeadObjectException) {
120 // System process is dead; ignore
121 } else {
122 try {
123 Clog_e(TAG, "Error reporting crash", t2);
124 } catch (Throwable t3) {
125 // Even Clog_e() fails! Oh well.
126 }
Dan Egnor60d87622009-12-16 16:32:58 -0800127 }
128 } finally {
129 // Try everything to make sure this process goes away.
130 Process.killProcess(Process.myPid());
131 System.exit(10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 }
134 }
135
136 private static final void commonInit() {
Jeff Brownebed7d62011-05-16 17:08:42 -0700137 if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
Tobias Thiereraddbf902016-07-21 15:05:19 +0100139 /*
140 * set handlers; these apply to all threads in the VM. Apps can replace
141 * the default handler, but not the pre handler.
142 */
143 Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
144 Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 /*
147 * Install a TimezoneGetter subclass for ZoneInfo.db
148 */
149 TimezoneGetter.setInstance(new TimezoneGetter() {
150 @Override
151 public String getId() {
152 return SystemProperties.get("persist.sys.timezone");
153 }
154 });
155 TimeZone.setDefault(null);
156
157 /*
158 * Sets handler for java.util.logging to use Android log facilities.
159 * The odd "new instance-and-then-throw-away" is a mirror of how
160 * the "java.util.logging.config.class" system property works. We
161 * can't use the system property here since the logger has almost
162 * certainly already been initialized.
163 */
164 LogManager.getLogManager().reset();
165 new AndroidConfig();
166
167 /*
Jesse Wilsond0f80d42009-09-18 18:06:43 -0700168 * Sets the default HTTP User-Agent used by HttpURLConnection.
169 */
170 String userAgent = getDefaultUserAgent();
171 System.setProperty("http.agent", userAgent);
172
173 /*
Jesse Wilson8568db52011-06-28 19:06:31 -0700174 * Wire socket tagging to traffic stats.
175 */
176 NetworkManagementSocketTagger.install();
177
178 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 * If we're running in an emulator launched with "-trace", put the
180 * VM into emulator trace profiling mode so that the user can hit
181 * F9/F10 at any time to capture traces. This has performance
182 * consequences, so it's not something you want to do always.
183 */
184 String trace = SystemProperties.get("ro.kernel.android.tracing");
185 if (trace.equals("1")) {
Dianne Hackbornc9421ba2010-03-11 22:23:46 -0800186 Slog.i(TAG, "NOTE: emulator trace profiling enabled");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 Debug.enableEmulatorTraceOutput();
188 }
189
190 initialized = true;
191 }
192
193 /**
Jesse Wilsond0f80d42009-09-18 18:06:43 -0700194 * Returns an HTTP user agent of the form
195 * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
196 */
197 private static String getDefaultUserAgent() {
198 StringBuilder result = new StringBuilder(64);
199 result.append("Dalvik/");
200 result.append(System.getProperty("java.vm.version")); // such as 1.1.0
201 result.append(" (Linux; U; Android ");
202
203 String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
204 result.append(version.length() > 0 ? version : "1.0");
205
206 // add the model for the release build
207 if ("REL".equals(Build.VERSION.CODENAME)) {
208 String model = Build.MODEL;
209 if (model.length() > 0) {
210 result.append("; ");
211 result.append(model);
212 }
213 }
214 String id = Build.ID; // "MASTER" or "M4-rc20"
215 if (id.length() > 0) {
216 result.append(" Build/");
217 result.append(id);
218 }
219 result.append(")");
220 return result.toString();
221 }
222
223 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 * Invokes a static "main(argv[]) method on class "className".
225 * Converts various failing exceptions into RuntimeExceptions, with
226 * the assumption that they will then cause the VM instance to exit.
227 *
228 * @param className Fully-qualified class name
229 * @param argv Argument vector for main()
Narayan Kamath29564cd2014-08-07 10:57:40 +0100230 * @param classLoader the classLoader to load {@className} with
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 */
Narayan Kamath29564cd2014-08-07 10:57:40 +0100232 private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
Tobias Sargeantdd4bb312016-01-19 16:34:54 +0000233 throws Zygote.MethodAndArgsCaller {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 Class<?> cl;
235
236 try {
Narayan Kamath29564cd2014-08-07 10:57:40 +0100237 cl = Class.forName(className, true, classLoader);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 } catch (ClassNotFoundException ex) {
239 throw new RuntimeException(
240 "Missing class when invoking static main " + className,
241 ex);
242 }
243
244 Method m;
245 try {
246 m = cl.getMethod("main", new Class[] { String[].class });
247 } catch (NoSuchMethodException ex) {
248 throw new RuntimeException(
249 "Missing static main on " + className, ex);
250 } catch (SecurityException ex) {
251 throw new RuntimeException(
252 "Problem getting static main on " + className, ex);
253 }
254
255 int modifiers = m.getModifiers();
256 if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
257 throw new RuntimeException(
258 "Main method is not public and static on " + className);
259 }
260
261 /*
262 * This throw gets caught in ZygoteInit.main(), which responds
263 * by invoking the exception's run() method. This arrangement
264 * clears up all the stack frames that were required in setting
265 * up the process.
266 */
Tobias Sargeantdd4bb312016-01-19 16:34:54 +0000267 throw new Zygote.MethodAndArgsCaller(m, argv);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 }
269
270 public static final void main(String[] argv) {
Piotr Jastrzebskida74a622015-02-12 13:55:23 +0000271 enableDdms();
Jeff Brownebed7d62011-05-16 17:08:42 -0700272 if (argv.length == 2 && argv[1].equals("application")) {
273 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
274 redirectLogStreams();
275 } else {
276 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
277 }
278
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 commonInit();
Bob Leee5408332009-09-04 18:31:17 -0700280
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 /*
282 * Now that we're running in interpreted code, call back into native code
283 * to run the system.
284 */
Jeff Brown16f5f5c2012-03-15 16:53:55 -0700285 nativeFinishInit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286
Jeff Brownebed7d62011-05-16 17:08:42 -0700287 if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
Bob Leee5408332009-09-04 18:31:17 -0700289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 /**
291 * The main function called when started through the zygote process. This
Jeff Brown16f5f5c2012-03-15 16:53:55 -0700292 * could be unified with main(), if the native code in nativeFinishInit()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 * were rationalized with Zygote startup.<p>
294 *
295 * Current recognized args:
296 * <ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 * <li> <code> [--] &lt;start class name&gt; &lt;args&gt;
298 * </ul>
299 *
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700300 * @param targetSdkVersion target SDK version
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 * @param argv arg strings
302 */
Narayan Kamath29564cd2014-08-07 10:57:40 +0100303 public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
Tobias Sargeantdd4bb312016-01-19 16:34:54 +0000304 throws Zygote.MethodAndArgsCaller {
Jeff Brownebed7d62011-05-16 17:08:42 -0700305 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
306
Narayan Kamathfbb32f62015-06-12 15:34:35 +0100307 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
Jeff Brownebed7d62011-05-16 17:08:42 -0700308 redirectLogStreams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309
310 commonInit();
Jeff Brown16f5f5c2012-03-15 16:53:55 -0700311 nativeZygoteInit();
Narayan Kamath29564cd2014-08-07 10:57:40 +0100312 applicationInit(targetSdkVersion, argv, classLoader);
Jeff Brownebed7d62011-05-16 17:08:42 -0700313 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314
Jeff Brownebed7d62011-05-16 17:08:42 -0700315 /**
316 * The main function called when an application is started through a
317 * wrapper process.
318 *
319 * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
320 * which calls {@link WrapperInit#main} which then calls this method.
321 * So we don't need to call commonInit() here.
322 *
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700323 * @param targetSdkVersion target SDK version
Jeff Brownebed7d62011-05-16 17:08:42 -0700324 * @param argv arg strings
325 */
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700326 public static void wrapperInit(int targetSdkVersion, String[] argv)
Tobias Sargeantdd4bb312016-01-19 16:34:54 +0000327 throws Zygote.MethodAndArgsCaller {
Jeff Brownebed7d62011-05-16 17:08:42 -0700328 if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329
Narayan Kamath29564cd2014-08-07 10:57:40 +0100330 applicationInit(targetSdkVersion, argv, null);
Jeff Brownebed7d62011-05-16 17:08:42 -0700331 }
332
Narayan Kamath29564cd2014-08-07 10:57:40 +0100333 private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
Tobias Sargeantdd4bb312016-01-19 16:34:54 +0000334 throws Zygote.MethodAndArgsCaller {
Jeff Brown4280c4a2012-03-15 17:48:02 -0700335 // If the application calls System.exit(), terminate the process
336 // immediately without running any shutdown hooks. It is not possible to
337 // shutdown an Android application gracefully. Among other things, the
338 // Android runtime shutdown hooks close the Binder driver, which can cause
339 // leftover running threads to crash before the process actually exits.
340 nativeSetExitWithoutCleanup(true);
341
Jeff Brownebed7d62011-05-16 17:08:42 -0700342 // We want to be fairly aggressive about heap utilization, to avoid
343 // holding on to a lot of memory that isn't needed.
344 VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700345 VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
Jeff Brownebed7d62011-05-16 17:08:42 -0700346
347 final Arguments args;
348 try {
349 args = new Arguments(argv);
350 } catch (IllegalArgumentException ex) {
351 Slog.e(TAG, ex.getMessage());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 // let the process exit
353 return;
354 }
355
Narayan Kamathfbb32f62015-06-12 15:34:35 +0100356 // The end of of the RuntimeInit event (see #zygoteInit).
357 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // Remaining arguments are passed to the start class's static main
Narayan Kamath29564cd2014-08-07 10:57:40 +0100360 invokeStaticMain(args.startClass, args.startArgs, classLoader);
Jeff Brownebed7d62011-05-16 17:08:42 -0700361 }
Bob Leee5408332009-09-04 18:31:17 -0700362
Jeff Brownebed7d62011-05-16 17:08:42 -0700363 /**
364 * Redirect System.out and System.err to the Android log.
365 */
366 public static void redirectLogStreams() {
367 System.out.close();
368 System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
369 System.err.close();
370 System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 }
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 /**
Dan Egnor60d87622009-12-16 16:32:58 -0800374 * Report a serious error in the current process. May or may not cause
375 * the process to terminate (depends on system settings).
Bob Leee5408332009-09-04 18:31:17 -0700376 *
Dan Egnor60d87622009-12-16 16:32:58 -0800377 * @param tag to record with the error
378 * @param t exception describing the error site and conditions
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 */
Dianne Hackborn52322712014-08-26 22:47:26 -0700380 public static void wtf(String tag, Throwable t, boolean system) {
Dan Egnor60d87622009-12-16 16:32:58 -0800381 try {
382 if (ActivityManagerNative.getDefault().handleApplicationWtf(
Dianne Hackborn52322712014-08-26 22:47:26 -0700383 mApplicationObject, tag, system, new ApplicationErrorReport.CrashInfo(t))) {
Dan Egnor60d87622009-12-16 16:32:58 -0800384 // The Activity Manager has already written us off -- now exit.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 Process.killProcess(Process.myPid());
386 System.exit(10);
387 }
Dan Egnor60d87622009-12-16 16:32:58 -0800388 } catch (Throwable t2) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700389 if (t2 instanceof DeadObjectException) {
390 // System process is dead; ignore
391 } else {
392 Slog.e(TAG, "Error reporting WTF", t2);
393 Slog.e(TAG, "Original WTF:", t);
394 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 }
396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 * Set the object identifying this application/process, for reporting VM
400 * errors.
401 */
402 public static final void setApplicationObject(IBinder app) {
403 mApplicationObject = app;
404 }
405
Brad Fitzpatrick438d0592010-06-10 12:19:19 -0700406 public static final IBinder getApplicationObject() {
407 return mApplicationObject;
408 }
409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 /**
Piotr Jastrzebskida74a622015-02-12 13:55:23 +0000411 * Enable DDMS.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 */
Piotr Jastrzebskida74a622015-02-12 13:55:23 +0000413 static final void enableDdms() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 // Register handlers for DDM messages.
415 android.ddm.DdmRegister.registerHandlers();
416 }
Jeff Brownebed7d62011-05-16 17:08:42 -0700417
418 /**
419 * Handles argument parsing for args related to the runtime.
420 *
421 * Current recognized args:
422 * <ul>
423 * <li> <code> [--] &lt;start class name&gt; &lt;args&gt;
424 * </ul>
425 */
426 static class Arguments {
427 /** first non-option argument */
428 String startClass;
429
430 /** all following arguments */
431 String[] startArgs;
432
433 /**
434 * Constructs instance and parses args
435 * @param args runtime command-line args
436 * @throws IllegalArgumentException
437 */
438 Arguments(String args[]) throws IllegalArgumentException {
439 parseArgs(args);
440 }
441
442 /**
443 * Parses the commandline arguments intended for the Runtime.
444 */
445 private void parseArgs(String args[])
446 throws IllegalArgumentException {
447 int curArg = 0;
448 for (; curArg < args.length; curArg++) {
449 String arg = args[curArg];
450
451 if (arg.equals("--")) {
452 curArg++;
453 break;
454 } else if (!arg.startsWith("--")) {
455 break;
456 }
457 }
458
459 if (curArg == args.length) {
460 throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
461 }
462
463 startClass = args[curArg++];
464 startArgs = new String[args.length - curArg];
465 System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
466 }
467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468}