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