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