DropBox logging of app & system server crashes.

The crashes are also reported to the event log (and of course the
main logcat, like they always have been).  Ordinary Log.e(t,m,e) isn't dropboxed
but there's a new Log.wtf() which always is.  (Still @pending in this change.)

Add a hook to IPowerManager to crash the system server on demand
(only for apps with REBOOT permission, since it's basically a restart).
This is not exposed in PowerManager, must be invoked directly -- mostly
this is there so "Bad Behavior" in dev tools can do it.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 676d6d5..7d07604 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -606,7 +606,7 @@
         public int uid;
         
         /**
-         * The tag that was provided when the process crashed.
+         * The activity name associated with the error, if known.  May be null.
          */
         public String tag;
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a0498aa..90f46dd 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -979,13 +979,23 @@
             return true;
         }
 
-        case HANDLE_APPLICATION_ERROR_TRANSACTION: {
+        case HANDLE_APPLICATION_CRASH_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder app = data.readStrongBinder();
+            ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
+            handleApplicationCrash(app, ci);
+            reply.writeNoException();
+            return true;
+        }
+
+        case HANDLE_APPLICATION_WTF_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder app = data.readStrongBinder();
             String tag = data.readString();
             ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
-            handleApplicationError(app, tag, ci);
+            boolean res = handleApplicationWtf(app, tag, ci);
             reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
             return true;
         }
 
@@ -2337,7 +2347,20 @@
         /* this base class version is never called */
         return true;
     }
-    public void handleApplicationError(IBinder app, String tag,
+    public void handleApplicationCrash(IBinder app,
+            ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(app);
+        crashInfo.writeToParcel(data, 0);
+        mRemote.transact(HANDLE_APPLICATION_CRASH_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+    public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -2346,10 +2369,12 @@
         data.writeStrongBinder(app);
         data.writeString(tag);
         crashInfo.writeToParcel(data, 0);
-        mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0);
+        mRemote.transact(HANDLE_APPLICATION_WTF_TRANSACTION, data, reply, 0);
         reply.readException();
+        boolean res = reply.readInt() != 0;
         reply.recycle();
         data.recycle();
+        return res;
     }
 
     public void signalPersistentProcesses(int sig) throws RemoteException {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index e89b3ad0..a4b692f 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -195,6 +195,7 @@
             StringWriter sw = new StringWriter();
             tr.printStackTrace(new PrintWriter(sw));
             stackTrace = sw.toString();
+            exceptionMessage = tr.getMessage();
 
             // Populate fields with the "root cause" exception
             while (tr.getCause() != null) {
diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl
index 804dd61..c76a517 100644
--- a/core/java/android/app/IActivityController.aidl
+++ b/core/java/android/app/IActivityController.aidl
@@ -44,7 +44,7 @@
      * it immediately.
      */
     boolean appCrashed(String processName, int pid,
-            String tag, String shortMsg, String longMsg,
+            String shortMsg, String longMsg,
             long timeMillis, String stackTrace);
     
     /**
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c890c4c..ca6bfa7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -242,8 +242,9 @@
     // Special low-level communication with activity manager.
     public void startRunning(String pkg, String cls, String action,
             String data) throws RemoteException;
-
-    public void handleApplicationError(IBinder app, String tag,
+    public void handleApplicationCrash(IBinder app,
+            ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
+    public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
     
     /*
@@ -349,7 +350,7 @@
     // Please keep these transaction codes the same -- they are also
     // sent by C++ code.
     int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
-    int HANDLE_APPLICATION_ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
+    int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
     int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
     int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
     int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
@@ -446,4 +447,5 @@
     int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
     int START_ACTIVITY_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99;
     int OVERRIDE_PENDING_TRANSITION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+100;
+    int HANDLE_APPLICATION_WTF_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+101;
 }
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 38d252e..3457815 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -103,9 +103,7 @@
             try {
                 observer.onEvent(mask, path);
             } catch (Throwable throwable) {
-                Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() + 
-                        " (returned by observer " + observer + ")", throwable);
-                RuntimeInit.crash("FileObserver", throwable);
+                Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
             }
         }
     }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 0afc537..23762ca 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -31,6 +31,7 @@
     void preventScreenOn(boolean prevent);
     boolean isScreenOn();
     void reboot(String reason);
+    void crash(String message);
 
     // sets the brightness of the backlights (screen, keyboard, button) 0-255
     void setBacklightBrightness(int brightness);
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index caf0923..bc653d6 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -115,9 +115,7 @@
                         didIdle = true;
                         keep = ((IdleHandler)idler).queueIdle();
                     } catch (Throwable t) {
-                        Log.e("MessageQueue",
-                              "IdleHandler threw exception", t);
-                        RuntimeInit.crash("MessageQueue", t);
+                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                     }
 
                     if (!keep) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 293dabc..e4eaf45b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -326,12 +326,11 @@
         {
             synchronized (mToken) {
                 if (mHeld) {
+                    Log.wtf(TAG, "WakeLock finalized while still held: " + mTag);
                     try {
                         mService.releaseWakeLock(mToken, 0);
                     } catch (RemoteException e) {
                     }
-                    RuntimeInit.crash(TAG, new Exception(
-                                "WakeLock finalized while still held: "+mTag));
                 }
             }
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 947ab19..09651f1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3710,6 +3710,11 @@
                 "dropbox:";
 
         /**
+         * Nonzero causes Log.wtf() to crash.
+         */
+        public static final String WTF_IS_FATAL = "wtf_is_fatal";
+
+        /**
          * The length of time in milli-seconds that automatic small adjustments to
          * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
          */
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index e95d0be..7a959a6 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -81,6 +81,13 @@
      */
     public static final int ASSERT = 7;
 
+    /**
+     * Exception class used to capture a stack trace in {@link #wtf()}.
+     */
+    private static class TerribleFailure extends Exception {
+        TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
+    }
+
     private Log() {
     }
 
@@ -170,24 +177,24 @@
 
     /**
      * Checks to see whether or not a log for the specified tag is loggable at the specified level.
-     * 
+     *
      *  The default level of any tag is set to INFO. This means that any level above and including
      *  INFO will be logged. Before you make any calls to a logging method you should check to see
      *  if your tag should be logged. You can change the default level by setting a system property:
      *      'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
-     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will 
+     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
      *  turn off all logging for your tag. You can also create a local.prop file that with the
      *  following in it:
      *      'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
      *  and place that in /data/local.prop.
-     *  
+     *
      * @param tag The tag to check.
      * @param level The level to check.
      * @return Whether or not that this is allowed to be logged.
      * @throws IllegalArgumentException is thrown if the tag.length() > 23.
      */
     public static native boolean isLoggable(String tag, int level);
-        
+
     /*
      * Send a {@link #WARN} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
@@ -220,6 +227,46 @@
     }
 
     /**
+     * What a Terrible Failure: Report a condition that should never happen.
+     * The error will always be logged at level ASSERT with the call stack.
+     * Depending on system configuration, a report may be added to the
+     * {@link android.os.DropBoxManager} and/or the process may be terminated
+     * immediately with an error dialog.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @pending
+     */
+    public static int wtf(String tag, String msg) {
+        return wtf(tag, msg, null);
+    }
+
+    /**
+     * What a Terrible Failure: Report an exception that should never happen.
+     * Similar to {@link #wtf(String, String)}, with an exception to log.
+     * @param tag Used to identify the source of a log message.
+     * @param tr An exception to log.
+     * @pending
+     */
+    public static int wtf(String tag, Throwable tr) {
+        return wtf(tag, tr.getMessage(), tr);
+    }
+
+    /**
+     * What a Terrible Failure: Report an exception that should never happen.
+     * Similar to {@link #wtf(String, Throwable)}, with a message as well.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param tr An exception to log.  May be null.
+     * @pending
+     */
+    public static int wtf(String tag, String msg, Throwable tr) {
+        tr = new TerribleFailure(msg, tr);
+        int bytes = println(ASSERT, tag, getStackTraceString(tr));
+        RuntimeInit.wtf(tag, tr);
+        return bytes;
+    }
+
+    /**
      * Handy function to get a loggable stack trace from a Throwable
      * @param tr An exception to log
      */
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index b7bb72d..57a28e6 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -58,6 +58,10 @@
     /** true if commonInit() has been called */
     private static boolean initialized;
 
+    private static IBinder mApplicationObject;
+
+    private static volatile boolean mCrashing = false;
+
     /**
      * Use this to log a message when a thread exits due to an uncaught
      * exception.  The framework catches these for the main threads, so
@@ -66,14 +70,30 @@
     private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
         public void uncaughtException(Thread t, Throwable e) {
             try {
-                Log.e(TAG, "Uncaught handler: thread " + t.getName()
-                        + " exiting due to uncaught exception");
-            } catch (Throwable error) {
-                // Ignore the throwable, since we're in the process of crashing anyway.
-                // If we don't, the crash won't happen properly and the process will
-                // be left around in a bad state.
+                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
+                if (mCrashing) return;
+                mCrashing = true;
+
+                if (mApplicationObject == null) {
+                    Log.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
+                } else {
+                    Log.e(TAG, "FATAL EXCEPTION: " + t.getName(), e);
+                }
+
+                // Bring up crash dialog, wait for it to be dismissed
+                ActivityManagerNative.getDefault().handleApplicationCrash(
+                        mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
+            } catch (Throwable t2) {
+                try {
+                    Log.e(TAG, "Error reporting crash", t2);
+                } catch (Throwable t3) {
+                    // Even Log.e() fails!  Oh well.
+                }
+            } finally {
+                // Try everything to make sure this process goes away.
+                Process.killProcess(Process.myPid());
+                System.exit(10);
             }
-            crash(TAG, e);
         }
     }
 
@@ -300,46 +320,22 @@
     public static native int getQwertyKeyboard();
 
     /**
-     * Report a fatal error in the current process.  If this is a user-process,
-     * a dialog may be displayed informing the user of the error.  This
-     * function does not return; it forces the current process to exit.
+     * Report a serious error in the current process.  May or may not cause
+     * the process to terminate (depends on system settings).
      *
-     * @param tag to use when logging the error
-     * @param t exception that was generated by the error
+     * @param tag to record with the error
+     * @param t exception describing the error site and conditions
      */
-    public static void crash(String tag, Throwable t) {
-        if (mApplicationObject != null) {
-            try {
-                // Log exception.
-                Log.e(TAG, Log.getStackTraceString(t));
-
-                // Show a message to the user.
-                IActivityManager am = ActivityManagerNative.getDefault();
-                am.handleApplicationError(mApplicationObject, tag,
-                        new ApplicationErrorReport.CrashInfo(t));
-            } catch (Throwable t2) {
-                try {
-                    // Log exception as a string so we don't get in an infinite loop.
-                    Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2));
-                } catch (Throwable t3) {
-                    // Do nothing, must be OOM so we can't format the message
-                }
-            } finally {
-                // Try everything to make sure this process goes away.
+    public static void wtf(String tag, Throwable t) {
+        try {
+            if (ActivityManagerNative.getDefault().handleApplicationWtf(
+                    mApplicationObject, tag, new ApplicationErrorReport.CrashInfo(t))) {
+                // The Activity Manager has already written us off -- now exit.
                 Process.killProcess(Process.myPid());
                 System.exit(10);
             }
-        } else {
-            try {
-                Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS.  System will crash.");
-                Log.e(tag, Log.getStackTraceString(t));
-            } catch (Throwable t2) {
-                // Do nothing, must be OOM so we can't format the message
-            } finally {
-                // Try everything to make sure this process goes away.
-                Process.killProcess(Process.myPid());
-                System.exit(10);
-            }
+        } catch (Throwable t2) {
+            Log.e(TAG, "Error reporting WTF", t2);
         }
     }
 
@@ -361,6 +357,4 @@
         // Register handlers for DDM messages.
         android.ddm.DdmRegister.registerHandlers();
     }
-
-    private static IBinder mApplicationObject;
 }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e11c312..d740ce1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -2137,6 +2137,24 @@
         }
     }
 
+    /**
+     * Crash the runtime (causing a complete restart of the Android framework).
+     * Requires REBOOT permission.  Mostly for testing.  Should not return.
+     */
+    public void crash(final String message)
+    {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+        Thread t = new Thread("PowerManagerService.crash()") {
+            public void run() { throw new RuntimeException(message); }
+        };
+        try {
+            t.start();
+            t.join();
+        } catch (InterruptedException e) {
+            Log.wtf(TAG, e);
+        }
+    }
+
     private void goToSleepLocked(long time, int reason) {
 
         if (mLastEventTime <= time) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 266f213..31edaf2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -71,7 +71,9 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Build;
 import android.os.Debug;
+import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -8580,11 +8582,10 @@
     }
 
     private boolean makeAppCrashingLocked(ProcessRecord app,
-            String tag, String shortMsg, String longMsg, String stackTrace) {
+            String shortMsg, String longMsg, String stackTrace) {
         app.crashing = true;
         app.crashingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg,
-                stackTrace);
+                ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
         startAppProblemLocked(app);
         app.stopFreezingAllLocked();
         return handleAppCrashLocked(app);
@@ -8658,10 +8659,11 @@
     }
 
     private void makeAppNotRespondingLocked(ProcessRecord app,
-            String tag, String shortMsg, String longMsg) {
+            String activity, String shortMsg, String longMsg) {
         app.notResponding = true;
         app.notRespondingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, null);
+                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+                activity, shortMsg, longMsg, null);
         startAppProblemLocked(app);
         app.stopFreezingAllLocked();
     }
@@ -8672,7 +8674,7 @@
      * @param app The ProcessRecord in which the error occurred.
      * @param condition Crashing, Application Not Responding, etc.  Values are defined in 
      *                      ActivityManager.AppErrorStateInfo
-     * @param tag The tag that was passed into handleApplicationError().  Typically the classname.
+     * @param activity The activity associated with the crash, if known.
      * @param shortMsg Short message describing the crash.
      * @param longMsg Long message describing the crash.
      * @param stackTrace Full crash stack trace, may be null.
@@ -8680,14 +8682,14 @@
      * @return Returns a fully-formed AppErrorStateInfo record.
      */
     private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, 
-            int condition, String tag, String shortMsg, String longMsg, String stackTrace) {
+            int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
         ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
 
         report.condition = condition;
         report.processName = app.processName;
         report.pid = app.pid;
         report.uid = app.info.uid;
-        report.tag = tag;
+        report.tag = activity;
         report.shortMsg = shortMsg;
         report.longMsg = longMsg;
         report.stackTrace = stackTrace;
@@ -8804,10 +8806,126 @@
         }
     }
 
-    public void handleApplicationError(IBinder app, String tag,
+    /**
+     * Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes.
+     * The application process will exit immediately after this call returns.
+     * @param app object of the crashing app, null for the system server
+     * @param crashInfo describing the exception
+     */
+    public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
+        ProcessRecord r = findAppProcess(app);
+
+        EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
+                app == null ? "system" : (r == null ? "unknown" : r.processName),
+                crashInfo.exceptionClassName,
+                crashInfo.exceptionMessage,
+                crashInfo.throwFileName,
+                crashInfo.throwLineNumber);
+
+        addExceptionToDropBox("crash", r, null, crashInfo);
+
+        crashApplication(r, crashInfo);
+    }
+
+    /**
+     * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
+     * @param app object of the crashing app, null for the system server
+     * @param tag reported by the caller
+     * @param crashInfo describing the context of the error
+     * @return true if the process should exit immediately (WTF is fatal)
+     */
+    public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) {
-        AppErrorResult result = new AppErrorResult();
-        ProcessRecord r = null;
+        ProcessRecord r = findAppProcess(app);
+
+        EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(),
+                app == null ? "system" : (r == null ? "unknown" : r.processName),
+                tag, crashInfo.exceptionMessage);
+
+        addExceptionToDropBox("wtf", r, tag, crashInfo);
+
+        if (Settings.Gservices.getInt(mContext.getContentResolver(),
+                Settings.Gservices.WTF_IS_FATAL, 0) != 0) {
+            crashApplication(r, crashInfo);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @param app object of some object (as stored in {@link com.android.internal.os.RuntimeInit})
+     * @return the corresponding {@link ProcessRecord} object, or null if none could be found
+     */
+    private ProcessRecord findAppProcess(IBinder app) {
+        if (app == null) {
+            return null;
+        }
+
+        synchronized (this) {
+            for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
+                final int NA = apps.size();
+                for (int ia=0; ia<NA; ia++) {
+                    ProcessRecord p = apps.valueAt(ia);
+                    if (p.thread != null && p.thread.asBinder() == app) {
+                        return p;
+                    }
+                }
+            }
+
+            Log.w(TAG, "Can't find mystery application: " + app);
+            return null;
+        }
+    }
+
+    /**
+     * Write a description of an exception (from a crash or WTF report) to the drop box.
+     * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
+     * @param r the process which crashed, null for the system server
+     * @param tag supplied by the application (in the case of WTF), or null
+     * @param crashInfo describing the exception
+     */
+    private void addExceptionToDropBox(String eventType, ProcessRecord r, String tag,
+            ApplicationErrorReport.CrashInfo crashInfo) {
+        String dropboxTag, processName;
+        if (r == null) {
+            dropboxTag = "system_server_" + eventType;
+        } else if ((r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            dropboxTag = "system_app_" + eventType;
+        } else {
+            dropboxTag = "data_app_" + eventType;
+        }
+
+        DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+        if (dbox != null && dbox.isTagEnabled(dropboxTag)) {
+            StringBuilder sb = new StringBuilder(1024);
+            sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+            if (r == null) {
+                sb.append("Process: system_server\n");
+            } else {
+                sb.append("Package: ").append(r.info.packageName).append("\n");
+                if (!r.processName.equals(r.info.packageName)) {
+                    sb.append("Process: ").append(r.processName).append("\n");
+                }
+            }
+            if (tag != null) {
+                sb.append("Tag: ").append(tag).append("\n");
+            }
+            if (crashInfo != null && crashInfo.stackTrace != null) {
+                sb.append("\n").append(crashInfo.stackTrace);
+            }
+            dbox.addText(dropboxTag, sb.toString());
+        }
+    }
+
+    /**
+     * Bring up the "unexpected error" dialog box for a crashing app.
+     * Deal with edge cases (intercepts from instrumented applications,
+     * ActivityController, error intent receivers, that sort of thing).
+     * @param r the application crashing
+     * @param crashInfo describing the failure
+     */
+    private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
         long timeMillis = System.currentTimeMillis();
         String shortMsg = crashInfo.exceptionClassName;
         String longMsg = crashInfo.exceptionMessage;
@@ -8818,20 +8936,8 @@
             longMsg = shortMsg;
         }
 
+        AppErrorResult result = new AppErrorResult();
         synchronized (this) {
-            if (app != null) {
-                for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
-                    final int NA = apps.size();
-                    for (int ia=0; ia<NA; ia++) {
-                        ProcessRecord p = apps.valueAt(ia);
-                        if (p.thread != null && p.thread.asBinder() == app) {
-                            r = p;
-                            break;
-                        }
-                    }
-                }
-            }
-
             if (r != null) {
                 // The application has crashed. Send the SIGQUIT to the process so
                 // that it can dump its state.
@@ -8844,7 +8950,7 @@
                 try {
                     String name = r != null ? r.processName : null;
                     int pid = r != null ? r.pid : Binder.getCallingPid();
-                    if (!mController.appCrashed(name, pid, tag,
+                    if (!mController.appCrashed(name, pid,
                             shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
                         Log.w(TAG, "Force-killing crashed app " + name
                                 + " at watcher's request");
@@ -8872,15 +8978,9 @@
                 return;
             }
 
-            if (r != null) {
-                if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, stackTrace)) {
-                    return;
-                }
-            } else {
-                Log.w(TAG, "Some application object " + app + " tag " + tag
-                        + " has crashed, but I don't know who it is.");
-                Log.w(TAG, "ShortMsg:" + shortMsg);
-                Log.w(TAG, "LongMsg:" + longMsg);
+            // If we can't identify the process or it's already exceeded its crash quota,
+            // quit right away without showing a crash dialog.
+            if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
                 Binder.restoreCallingIdentity(origId);
                 return;
             }
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 7e4ea35..952555b 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -79,4 +79,7 @@
 # The activity manager gave up on a new process taking too long to start
 30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3)
 
-
+# Unhandled exception
+30039 am_crash (PID|1|5),(Process Name|3),(Exception|3),(Message|3),(File|3),(Line|1|5)
+# Log.wtf() called
+30040 am_wtf (PID|1|5),(Process Name|3),(Tag|3),(Message|3)