• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IActivityManager;
21 import android.os.Debug;
22 import android.os.IBinder;
23 import android.os.ICheckinService;
24 import android.os.Process;
25 import android.os.RemoteException;
26 import android.os.ServiceManager;
27 import android.os.SystemProperties;
28 import android.os.Build;
29 import android.server.data.CrashData;
30 import android.util.Config;
31 import android.util.Log;
32 
33 import com.android.internal.logging.AndroidConfig;
34 
35 import dalvik.system.VMRuntime;
36 
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.io.DataInputStream;
40 import java.io.DataOutputStream;
41 import java.io.IOException;
42 import java.lang.reflect.Method;
43 import java.lang.reflect.Modifier;
44 import java.util.concurrent.atomic.AtomicInteger;
45 import java.util.logging.LogManager;
46 import java.util.TimeZone;
47 
48 import org.apache.harmony.luni.internal.util.TimezoneGetter;
49 
50 /**
51  * Main entry point for runtime initialization.  Not for
52  * public consumption.
53  * @hide
54  */
55 public class RuntimeInit {
56     private final static String TAG = "AndroidRuntime";
57 
58     /** true if commonInit() has been called */
59     private static boolean initialized;
60 
61     /**
62      * Use this to log a message when a thread exits due to an uncaught
63      * exception.  The framework catches these for the main threads, so
64      * this should only matter for threads created by applications.
65      */
66     private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
uncaughtException(Thread t, Throwable e)67         public void uncaughtException(Thread t, Throwable e) {
68             try {
69                 Log.e(TAG, "Uncaught handler: thread " + t.getName()
70                         + " exiting due to uncaught exception");
71             } catch (Throwable error) {
72                 // Ignore the throwable, since we're in the process of crashing anyway.
73                 // If we don't, the crash won't happen properly and the process will
74                 // be left around in a bad state.
75             }
76             crash(TAG, e);
77         }
78     }
79 
commonInit()80     private static final void commonInit() {
81         if (Config.LOGV) Log.d(TAG, "Entered RuntimeInit!");
82 
83         /* set default handler; this applies to all threads in the VM */
84         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
85 
86         int hasQwerty = getQwertyKeyboard();
87 
88         if (Config.LOGV) Log.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
89         if (hasQwerty == 1) {
90             System.setProperty("qwerty", "1");
91         }
92 
93         /*
94          * Install a TimezoneGetter subclass for ZoneInfo.db
95          */
96         TimezoneGetter.setInstance(new TimezoneGetter() {
97             @Override
98             public String getId() {
99                 return SystemProperties.get("persist.sys.timezone");
100             }
101         });
102         TimeZone.setDefault(null);
103 
104         /*
105          * Sets handler for java.util.logging to use Android log facilities.
106          * The odd "new instance-and-then-throw-away" is a mirror of how
107          * the "java.util.logging.config.class" system property works. We
108          * can't use the system property here since the logger has almost
109          * certainly already been initialized.
110          */
111         LogManager.getLogManager().reset();
112         new AndroidConfig();
113 
114         /*
115          * Sets the default HTTP User-Agent used by HttpURLConnection.
116          */
117         String userAgent = getDefaultUserAgent();
118         System.setProperty("http.agent", userAgent);
119 
120         /*
121          * If we're running in an emulator launched with "-trace", put the
122          * VM into emulator trace profiling mode so that the user can hit
123          * F9/F10 at any time to capture traces.  This has performance
124          * consequences, so it's not something you want to do always.
125          */
126         String trace = SystemProperties.get("ro.kernel.android.tracing");
127         if (trace.equals("1")) {
128             Log.i(TAG, "NOTE: emulator trace profiling enabled");
129             Debug.enableEmulatorTraceOutput();
130         }
131 
132         initialized = true;
133     }
134 
135     /**
136      * Returns an HTTP user agent of the form
137      * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
138      */
getDefaultUserAgent()139     private static String getDefaultUserAgent() {
140         StringBuilder result = new StringBuilder(64);
141         result.append("Dalvik/");
142         result.append(System.getProperty("java.vm.version")); // such as 1.1.0
143         result.append(" (Linux; U; Android ");
144 
145         String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
146         result.append(version.length() > 0 ? version : "1.0");
147 
148         // add the model for the release build
149         if ("REL".equals(Build.VERSION.CODENAME)) {
150             String model = Build.MODEL;
151             if (model.length() > 0) {
152                 result.append("; ");
153                 result.append(model);
154             }
155         }
156         String id = Build.ID; // "MASTER" or "M4-rc20"
157         if (id.length() > 0) {
158             result.append(" Build/");
159             result.append(id);
160         }
161         result.append(")");
162         return result.toString();
163     }
164 
165     /**
166      * Invokes a static "main(argv[]) method on class "className".
167      * Converts various failing exceptions into RuntimeExceptions, with
168      * the assumption that they will then cause the VM instance to exit.
169      *
170      * @param className Fully-qualified class name
171      * @param argv Argument vector for main()
172      */
invokeStaticMain(String className, String[] argv)173     private static void invokeStaticMain(String className, String[] argv)
174             throws ZygoteInit.MethodAndArgsCaller {
175 
176         // We want to be fairly aggressive about heap utilization, to avoid
177         // holding on to a lot of memory that isn't needed.
178         VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
179 
180         Class<?> cl;
181 
182         try {
183             cl = Class.forName(className);
184         } catch (ClassNotFoundException ex) {
185             throw new RuntimeException(
186                     "Missing class when invoking static main " + className,
187                     ex);
188         }
189 
190         Method m;
191         try {
192             m = cl.getMethod("main", new Class[] { String[].class });
193         } catch (NoSuchMethodException ex) {
194             throw new RuntimeException(
195                     "Missing static main on " + className, ex);
196         } catch (SecurityException ex) {
197             throw new RuntimeException(
198                     "Problem getting static main on " + className, ex);
199         }
200 
201         int modifiers = m.getModifiers();
202         if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
203             throw new RuntimeException(
204                     "Main method is not public and static on " + className);
205         }
206 
207         /*
208          * This throw gets caught in ZygoteInit.main(), which responds
209          * by invoking the exception's run() method. This arrangement
210          * clears up all the stack frames that were required in setting
211          * up the process.
212          */
213         throw new ZygoteInit.MethodAndArgsCaller(m, argv);
214     }
215 
main(String[] argv)216     public static final void main(String[] argv) {
217         commonInit();
218 
219         /*
220          * Now that we're running in interpreted code, call back into native code
221          * to run the system.
222          */
223         finishInit();
224 
225         if (Config.LOGV) Log.d(TAG, "Leaving RuntimeInit!");
226     }
227 
finishInit()228     public static final native void finishInit();
229 
230     /**
231      * The main function called when started through the zygote process. This
232      * could be unified with main(), if the native code in finishInit()
233      * were rationalized with Zygote startup.<p>
234      *
235      * Current recognized args:
236      * <ul>
237      *   <li> --nice-name=<i>nice name to appear in ps</i>
238      *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
239      * </ul>
240      *
241      * @param argv arg strings
242      */
zygoteInit(String[] argv)243     public static final void zygoteInit(String[] argv)
244             throws ZygoteInit.MethodAndArgsCaller {
245         // TODO: Doing this here works, but it seems kind of arbitrary. Find
246         // a better place. The goal is to set it up for applications, but not
247         // tools like am.
248         System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
249         System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
250 
251         commonInit();
252         zygoteInitNative();
253 
254         int curArg = 0;
255         for ( /* curArg */ ; curArg < argv.length; curArg++) {
256             String arg = argv[curArg];
257 
258             if (arg.equals("--")) {
259                 curArg++;
260                 break;
261             } else if (!arg.startsWith("--")) {
262                 break;
263             } else if (arg.startsWith("--nice-name=")) {
264                 String niceName = arg.substring(arg.indexOf('=') + 1);
265                 Process.setArgV0(niceName);
266             }
267         }
268 
269         if (curArg == argv.length) {
270             Log.e(TAG, "Missing classname argument to RuntimeInit!");
271             // let the process exit
272             return;
273         }
274 
275         // Remaining arguments are passed to the start class's static main
276 
277         String startClass = argv[curArg++];
278         String[] startArgs = new String[argv.length - curArg];
279 
280         System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
281         invokeStaticMain(startClass, startArgs);
282     }
283 
zygoteInitNative()284     public static final native void zygoteInitNative();
285 
286     /**
287      * Returns 1 if the computer is on. If the computer isn't on, the value returned by this method is undefined.
288      */
isComputerOn()289     public static final native int isComputerOn();
290 
291     /**
292      * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined.
293      */
turnComputerOn()294     public static final native void turnComputerOn();
295 
296     /**
297      *
298      * @return 1 if the device has a qwerty keyboard
299      */
getQwertyKeyboard()300     public static native int getQwertyKeyboard();
301 
302     /**
303      * Report a fatal error in the current process.  If this is a user-process,
304      * a dialog may be displayed informing the user of the error.  This
305      * function does not return; it forces the current process to exit.
306      *
307      * @param tag to use when logging the error
308      * @param t exception that was generated by the error
309      */
crash(String tag, Throwable t)310     public static void crash(String tag, Throwable t) {
311         if (mApplicationObject != null) {
312             byte[] crashData = null;
313             try {
314                 // Log exception.
315                 Log.e(TAG, Log.getStackTraceString(t));
316                 crashData = marshallException(tag, t);
317                 if (crashData == null) {
318                     throw new NullPointerException("Can't marshall crash data");
319                 }
320             } catch (Throwable t2) {
321                 try {
322                     // Log exception as a string so we don't get in an infinite loop.
323                     Log.e(TAG, "Error reporting crash: "
324                             + Log.getStackTraceString(t2));
325                 } catch (Throwable t3) {
326                     // Do nothing, must be OOM so we can't format the message
327                 }
328             }
329 
330             try {
331                 // Display user-visible error message.
332                 String msg = t.getMessage();
333                 if (msg == null) {
334                     msg = t.toString();
335                 }
336 
337                 IActivityManager am = ActivityManagerNative.getDefault();
338                 try {
339                     int res = am.handleApplicationError(mApplicationObject,
340                             0, tag, msg, t.toString(), crashData);
341                     // Is waiting for the debugger the right thing?
342                     // For now I have turned off the Debug button, because
343                     // I'm not sure what we should do if it is actually
344                     // selected.
345                     //Log.i(TAG, "Got app error result: " + res);
346                     if (res == 1) {
347                         Debug.waitForDebugger();
348                         return;
349                     }
350                 } catch (RemoteException e) {
351                 }
352             } catch (Throwable t2) {
353                 try {
354                     // Log exception as a string so we don't get in an infinite loop.
355                     Log.e(TAG, "Error reporting crash: "
356                             + Log.getStackTraceString(t2));
357                 } catch (Throwable t3) {
358                     // Do nothing, must be OOM so we can't format the message
359                 }
360             } finally {
361                 // Try everything to make sure this process goes away.
362                 Process.killProcess(Process.myPid());
363                 System.exit(10);
364             }
365         } else {
366             try {
367                 Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS.  System will crash.");
368                 Log.e(tag, Log.getStackTraceString(t));
369                 reportException(tag, t, true);  // synchronous
370             } catch (Throwable t2) {
371                 // Do nothing, must be OOM so we can't format the message
372             } finally {
373                 // Try everything to make sure this process goes away.
374                 Process.killProcess(Process.myPid());
375                 System.exit(10);
376             }
377         }
378     }
379 
380     /** Counter used to prevent reentrancy in {@link #reportException}. */
381     private static final AtomicInteger sInReportException = new AtomicInteger();
382 
383     /**
384      * Report an error in the current process.  The exception information will
385      * be handed off to the checkin service and eventually uploaded for analysis.
386      * This is expensive!  Only use this when the exception indicates a programming
387      * error ("should not happen").
388      *
389      * @param tag to use when logging the error
390      * @param t exception that was generated by the error
391      * @param sync true to wait for the report, false to "fire and forget"
392      */
reportException(String tag, Throwable t, boolean sync)393     public static void reportException(String tag, Throwable t, boolean sync) {
394         if (!initialized) {
395             // Exceptions during, eg, zygote cannot use this mechanism
396             return;
397         }
398 
399         // It's important to prevent an infinite crash-reporting loop:
400         // while this function is running, don't let it be called again.
401         int reenter = sInReportException.getAndIncrement();
402         if (reenter != 0) {
403             sInReportException.decrementAndGet();
404             Log.e(TAG, "Crash logging skipped, already logging another crash");
405             return;
406         }
407 
408         // TODO: Enable callers to specify a level (i.e. warn or error).
409         try {
410             // Submit crash data to statistics service.
411             byte[] crashData = marshallException(tag, t);
412             ICheckinService checkin = ICheckinService.Stub.asInterface(
413                     ServiceManager.getService("checkin"));
414             if (checkin == null) {
415                 Log.e(TAG, "Crash logging skipped, no checkin service");
416             } else if (sync) {
417                 checkin.reportCrashSync(crashData);
418             } else {
419                 checkin.reportCrashAsync(crashData);
420             }
421         } catch (Throwable t2) {
422             // Log exception as a string so we don't get in an infinite loop.
423             Log.e(TAG, "Crash logging failed: " + t2);
424         } finally {
425             sInReportException.decrementAndGet();
426         }
427     }
428 
marshallException(String tag, Throwable t)429     private static byte[] marshallException(String tag, Throwable t) {
430         // Convert crash data to bytes.
431         ByteArrayOutputStream bout = new ByteArrayOutputStream();
432         DataOutputStream dout = new DataOutputStream(bout);
433         try {
434             new CrashData(tag, t).write(dout);
435             dout.close();
436         } catch (IOException e) {
437             return null;
438         }
439         return bout.toByteArray();
440     }
441 
442     /**
443      * Replay an encoded CrashData record back into a useable CrashData record.  This can be
444      * helpful for providing debugging output after a process error.
445      *
446      * @param crashDataBytes The byte array containing the encoded crash record
447      * @return new CrashData record, or null if could not create one.
448      */
unmarshallException(byte[] crashDataBytes)449     public static CrashData unmarshallException(byte[] crashDataBytes) {
450         try {
451             ByteArrayInputStream bin = new ByteArrayInputStream(crashDataBytes);
452             DataInputStream din = new DataInputStream(bin);
453             return new CrashData(din);
454         } catch (IOException e) {
455             return null;
456         }
457     }
458 
459     /**
460      * Set the object identifying this application/process, for reporting VM
461      * errors.
462      */
setApplicationObject(IBinder app)463     public static final void setApplicationObject(IBinder app) {
464         mApplicationObject = app;
465     }
466 
467     /**
468      * Enable debugging features.
469      */
470     static {
471         // Register handlers for DDM messages.
android.ddm.DdmRegister.registerHandlers()472         android.ddm.DdmRegister.registerHandlers();
473     }
474 
475     private static IBinder mApplicationObject;
476 
477 }
478