1 /*
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 
17 package com.android.internal.os;
18 
19 import android.app.ActivityManagerNative;
20 import android.app.ActivityThread;
21 import android.app.ApplicationErrorReport;
22 import android.os.Build;
23 import android.os.DeadObjectException;
24 import android.os.Debug;
25 import android.os.IBinder;
26 import android.os.Process;
27 import android.os.SystemProperties;
28 import android.os.Trace;
29 import android.util.Log;
30 import android.util.Slog;
31 import com.android.internal.logging.AndroidConfig;
32 import com.android.server.NetworkManagementSocketTagger;
33 import dalvik.system.VMRuntime;
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Modifier;
36 import java.util.TimeZone;
37 import java.util.logging.LogManager;
38 import org.apache.harmony.luni.internal.util.TimezoneGetter;
39 
40 /**
41  * Main entry point for runtime initialization.  Not for
42  * public consumption.
43  * @hide
44  */
45 public class RuntimeInit {
46     private final static String TAG = "AndroidRuntime";
47     private final static boolean DEBUG = false;
48 
49     /** true if commonInit() has been called */
50     private static boolean initialized;
51 
52     private static IBinder mApplicationObject;
53 
54     private static volatile boolean mCrashing = false;
55 
nativeZygoteInit()56     private static final native void nativeZygoteInit();
nativeFinishInit()57     private static final native void nativeFinishInit();
nativeSetExitWithoutCleanup(boolean exitWithoutCleanup)58     private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
59 
Clog_e(String tag, String msg, Throwable tr)60     private static int Clog_e(String tag, String msg, Throwable tr) {
61         return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
62     }
63 
64     /**
65      * Use this to log a message when a thread exits due to an uncaught
66      * exception.  The framework catches these for the main threads, so
67      * this should only matter for threads created by applications.
68      */
69     private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
uncaughtException(Thread t, Throwable e)70         public void uncaughtException(Thread t, Throwable e) {
71             try {
72                 // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
73                 if (mCrashing) return;
74                 mCrashing = true;
75 
76                 if (mApplicationObject == null) {
77                     Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
78                 } else {
79                     StringBuilder message = new StringBuilder();
80                     message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
81                     final String processName = ActivityThread.currentProcessName();
82                     if (processName != null) {
83                         message.append("Process: ").append(processName).append(", ");
84                     }
85                     message.append("PID: ").append(Process.myPid());
86                     Clog_e(TAG, message.toString(), e);
87                 }
88 
89                 // Try to end profiling. If a profiler is running at this point, and we kill the
90                 // process (below), the in-memory buffer will be lost. So try to stop, which will
91                 // flush the buffer. (This makes method trace profiling useful to debug crashes.)
92                 if (ActivityThread.currentActivityThread() != null) {
93                     ActivityThread.currentActivityThread().stopProfiling();
94                 }
95 
96                 // Bring up crash dialog, wait for it to be dismissed
97                 ActivityManagerNative.getDefault().handleApplicationCrash(
98                         mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
99             } catch (Throwable t2) {
100                 if (t2 instanceof DeadObjectException) {
101                     // System process is dead; ignore
102                 } else {
103                     try {
104                         Clog_e(TAG, "Error reporting crash", t2);
105                     } catch (Throwable t3) {
106                         // Even Clog_e() fails!  Oh well.
107                     }
108                 }
109             } finally {
110                 // Try everything to make sure this process goes away.
111                 Process.killProcess(Process.myPid());
112                 System.exit(10);
113             }
114         }
115     }
116 
commonInit()117     private static final void commonInit() {
118         if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
119 
120         /* set default handler; this applies to all threads in the VM */
121         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
122 
123         /*
124          * Install a TimezoneGetter subclass for ZoneInfo.db
125          */
126         TimezoneGetter.setInstance(new TimezoneGetter() {
127             @Override
128             public String getId() {
129                 return SystemProperties.get("persist.sys.timezone");
130             }
131         });
132         TimeZone.setDefault(null);
133 
134         /*
135          * Sets handler for java.util.logging to use Android log facilities.
136          * The odd "new instance-and-then-throw-away" is a mirror of how
137          * the "java.util.logging.config.class" system property works. We
138          * can't use the system property here since the logger has almost
139          * certainly already been initialized.
140          */
141         LogManager.getLogManager().reset();
142         new AndroidConfig();
143 
144         /*
145          * Sets the default HTTP User-Agent used by HttpURLConnection.
146          */
147         String userAgent = getDefaultUserAgent();
148         System.setProperty("http.agent", userAgent);
149 
150         /*
151          * Wire socket tagging to traffic stats.
152          */
153         NetworkManagementSocketTagger.install();
154 
155         /*
156          * If we're running in an emulator launched with "-trace", put the
157          * VM into emulator trace profiling mode so that the user can hit
158          * F9/F10 at any time to capture traces.  This has performance
159          * consequences, so it's not something you want to do always.
160          */
161         String trace = SystemProperties.get("ro.kernel.android.tracing");
162         if (trace.equals("1")) {
163             Slog.i(TAG, "NOTE: emulator trace profiling enabled");
164             Debug.enableEmulatorTraceOutput();
165         }
166 
167         initialized = true;
168     }
169 
170     /**
171      * Returns an HTTP user agent of the form
172      * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
173      */
getDefaultUserAgent()174     private static String getDefaultUserAgent() {
175         StringBuilder result = new StringBuilder(64);
176         result.append("Dalvik/");
177         result.append(System.getProperty("java.vm.version")); // such as 1.1.0
178         result.append(" (Linux; U; Android ");
179 
180         String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
181         result.append(version.length() > 0 ? version : "1.0");
182 
183         // add the model for the release build
184         if ("REL".equals(Build.VERSION.CODENAME)) {
185             String model = Build.MODEL;
186             if (model.length() > 0) {
187                 result.append("; ");
188                 result.append(model);
189             }
190         }
191         String id = Build.ID; // "MASTER" or "M4-rc20"
192         if (id.length() > 0) {
193             result.append(" Build/");
194             result.append(id);
195         }
196         result.append(")");
197         return result.toString();
198     }
199 
200     /**
201      * Invokes a static "main(argv[]) method on class "className".
202      * Converts various failing exceptions into RuntimeExceptions, with
203      * the assumption that they will then cause the VM instance to exit.
204      *
205      * @param className Fully-qualified class name
206      * @param argv Argument vector for main()
207      * @param classLoader the classLoader to load {@className} with
208      */
invokeStaticMain(String className, String[] argv, ClassLoader classLoader)209     private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
210             throws ZygoteInit.MethodAndArgsCaller {
211         Class<?> cl;
212 
213         try {
214             cl = Class.forName(className, true, classLoader);
215         } catch (ClassNotFoundException ex) {
216             throw new RuntimeException(
217                     "Missing class when invoking static main " + className,
218                     ex);
219         }
220 
221         Method m;
222         try {
223             m = cl.getMethod("main", new Class[] { String[].class });
224         } catch (NoSuchMethodException ex) {
225             throw new RuntimeException(
226                     "Missing static main on " + className, ex);
227         } catch (SecurityException ex) {
228             throw new RuntimeException(
229                     "Problem getting static main on " + className, ex);
230         }
231 
232         int modifiers = m.getModifiers();
233         if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
234             throw new RuntimeException(
235                     "Main method is not public and static on " + className);
236         }
237 
238         /*
239          * This throw gets caught in ZygoteInit.main(), which responds
240          * by invoking the exception's run() method. This arrangement
241          * clears up all the stack frames that were required in setting
242          * up the process.
243          */
244         throw new ZygoteInit.MethodAndArgsCaller(m, argv);
245     }
246 
main(String[] argv)247     public static final void main(String[] argv) {
248         enableDdms();
249         if (argv.length == 2 && argv[1].equals("application")) {
250             if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
251             redirectLogStreams();
252         } else {
253             if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
254         }
255 
256         commonInit();
257 
258         /*
259          * Now that we're running in interpreted code, call back into native code
260          * to run the system.
261          */
262         nativeFinishInit();
263 
264         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
265     }
266 
267     /**
268      * The main function called when started through the zygote process. This
269      * could be unified with main(), if the native code in nativeFinishInit()
270      * were rationalized with Zygote startup.<p>
271      *
272      * Current recognized args:
273      * <ul>
274      *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
275      * </ul>
276      *
277      * @param targetSdkVersion target SDK version
278      * @param argv arg strings
279      */
zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)280     public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
281             throws ZygoteInit.MethodAndArgsCaller {
282         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
283 
284         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
285         redirectLogStreams();
286 
287         commonInit();
288         nativeZygoteInit();
289         applicationInit(targetSdkVersion, argv, classLoader);
290     }
291 
292     /**
293      * The main function called when an application is started through a
294      * wrapper process.
295      *
296      * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
297      * which calls {@link WrapperInit#main} which then calls this method.
298      * So we don't need to call commonInit() here.
299      *
300      * @param targetSdkVersion target SDK version
301      * @param argv arg strings
302      */
wrapperInit(int targetSdkVersion, String[] argv)303     public static void wrapperInit(int targetSdkVersion, String[] argv)
304             throws ZygoteInit.MethodAndArgsCaller {
305         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
306 
307         applicationInit(targetSdkVersion, argv, null);
308     }
309 
applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)310     private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
311             throws ZygoteInit.MethodAndArgsCaller {
312         // If the application calls System.exit(), terminate the process
313         // immediately without running any shutdown hooks.  It is not possible to
314         // shutdown an Android application gracefully.  Among other things, the
315         // Android runtime shutdown hooks close the Binder driver, which can cause
316         // leftover running threads to crash before the process actually exits.
317         nativeSetExitWithoutCleanup(true);
318 
319         // We want to be fairly aggressive about heap utilization, to avoid
320         // holding on to a lot of memory that isn't needed.
321         VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
322         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
323 
324         final Arguments args;
325         try {
326             args = new Arguments(argv);
327         } catch (IllegalArgumentException ex) {
328             Slog.e(TAG, ex.getMessage());
329             // let the process exit
330             return;
331         }
332 
333         // The end of of the RuntimeInit event (see #zygoteInit).
334         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
335 
336         // Remaining arguments are passed to the start class's static main
337         invokeStaticMain(args.startClass, args.startArgs, classLoader);
338     }
339 
340     /**
341      * Redirect System.out and System.err to the Android log.
342      */
redirectLogStreams()343     public static void redirectLogStreams() {
344         System.out.close();
345         System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
346         System.err.close();
347         System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
348     }
349 
350     /**
351      * Report a serious error in the current process.  May or may not cause
352      * the process to terminate (depends on system settings).
353      *
354      * @param tag to record with the error
355      * @param t exception describing the error site and conditions
356      */
wtf(String tag, Throwable t, boolean system)357     public static void wtf(String tag, Throwable t, boolean system) {
358         try {
359             if (ActivityManagerNative.getDefault().handleApplicationWtf(
360                     mApplicationObject, tag, system, new ApplicationErrorReport.CrashInfo(t))) {
361                 // The Activity Manager has already written us off -- now exit.
362                 Process.killProcess(Process.myPid());
363                 System.exit(10);
364             }
365         } catch (Throwable t2) {
366             if (t2 instanceof DeadObjectException) {
367                 // System process is dead; ignore
368             } else {
369                 Slog.e(TAG, "Error reporting WTF", t2);
370                 Slog.e(TAG, "Original WTF:", t);
371             }
372         }
373     }
374 
375     /**
376      * Set the object identifying this application/process, for reporting VM
377      * errors.
378      */
setApplicationObject(IBinder app)379     public static final void setApplicationObject(IBinder app) {
380         mApplicationObject = app;
381     }
382 
getApplicationObject()383     public static final IBinder getApplicationObject() {
384         return mApplicationObject;
385     }
386 
387     /**
388      * Enable DDMS.
389      */
enableDdms()390     static final void enableDdms() {
391         // Register handlers for DDM messages.
392         android.ddm.DdmRegister.registerHandlers();
393     }
394 
395     /**
396      * Handles argument parsing for args related to the runtime.
397      *
398      * Current recognized args:
399      * <ul>
400      *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
401      * </ul>
402      */
403     static class Arguments {
404         /** first non-option argument */
405         String startClass;
406 
407         /** all following arguments */
408         String[] startArgs;
409 
410         /**
411          * Constructs instance and parses args
412          * @param args runtime command-line args
413          * @throws IllegalArgumentException
414          */
Arguments(String args[])415         Arguments(String args[]) throws IllegalArgumentException {
416             parseArgs(args);
417         }
418 
419         /**
420          * Parses the commandline arguments intended for the Runtime.
421          */
parseArgs(String args[])422         private void parseArgs(String args[])
423                 throws IllegalArgumentException {
424             int curArg = 0;
425             for (; curArg < args.length; curArg++) {
426                 String arg = args[curArg];
427 
428                 if (arg.equals("--")) {
429                     curArg++;
430                     break;
431                 } else if (!arg.startsWith("--")) {
432                     break;
433                 }
434             }
435 
436             if (curArg == args.length) {
437                 throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
438             }
439 
440             startClass = args[curArg++];
441             startArgs = new String[args.length - curArg];
442             System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
443         }
444     }
445 }
446