public void addErrorToDropBox(String eventType, ProcessRecord process, String processName, ActivityRecord activity, ActivityRecord parent, String subject, final String report, final File logFile, final ApplicationErrorReport.CrashInfo crashInfo) { // NOTE -- this must never acquire the ActivityManagerService lock, // otherwise the watchdog may be prevented from resetting the system.
final String dropboxTag = processClass(process) + "_" + eventType; final DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
// Exit early if the dropbox isn't configured to accept this report type. if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
final StringBuilder sb = new StringBuilder(1024); appendDropBoxProcessHeaders(process, processName, sb); if (activity != null) { sb.append("Activity: ").append(activity.shortComponentName).append("\n"); } if (parent != null && parent.app != null && parent.app.pid != process.pid) { sb.append("Parent-Process: ").append(parent.app.processName).append("\n"); } if (parent != null && parent != activity) { sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n"); } if (subject != null) { sb.append("Subject: ").append(subject).append("\n"); } sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); if (Debug.isDebuggerConnected()) { sb.append("Debugger: Connected\n"); } sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O // (After this point, we shouldn't access AMS internal data structures.) Thread worker = new Thread("Error dump: " + dropboxTag) { @Override public void run() { if (report != null) { sb.append(report); } if (logFile != null) { try { sb.append(FileUtils.readTextFile(logFile, DROPBOX_MAX_SIZE, "\n\n[[TRUNCATED]]")); } catch (IOException e) { Slog.e(TAG, "Error reading " + logFile, e); } } if (crashInfo != null && crashInfo.stackTrace != null) { sb.append(crashInfo.stackTrace); }
String setting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag; int lines = Settings.Global.getInt(mContext.getContentResolver(), setting, 0); if (lines > 0) { sb.append("\n");
// Merge several logcat streams, and take the last N lines InputStreamReader input = null; try { java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat", "-v", "time", "-b", "events", "-b", "system", "-b", "main", "-b", "crash", "-t", String.valueOf(lines)).redirectErrorStream(true).start();
if (process == null) { // If process is null, we are being called from some internal code // and may be about to die -- run this synchronously. worker.run(); } else { worker.start(); } }
AppErrorResult result = new AppErrorResult(); synchronized (this) { if (mController != null) { try { String name = r != null ? r.processName : null; int pid = r != null ? r.pid : Binder.getCallingPid(); int uid = r != null ? r.info.uid : Binder.getCallingUid(); if (!mController.appCrashed(name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")) && "Native crash".equals(crashInfo.exceptionClassName)) { Slog.w(TAG, "Skip killing native crashed app " + name + "(" + pid + ") during testing"); } else { Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request"); if (r != null) { r.kill("crash", true); } else { // Huh. Process.killProcess(pid); killProcessGroup(uid, pid); } } return; } } catch (RemoteException e) { mController = null; Watchdog.getInstance().setActivityController(null); } }
final long origId = Binder.clearCallingIdentity();
// If this process is running instrumentation, finish it. if (r != null && r.instrumentationClass != null) { Slog.w(TAG, "Error in app " + r.processName + " running instrumentation " + r.instrumentationClass + ":"); if (shortMsg != null) Slog.w(TAG, " " + shortMsg); if (longMsg != null) Slog.w(TAG, " " + longMsg); Bundle info = new Bundle(); info.putString("shortMsg", shortMsg); info.putString("longMsg", longMsg); finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); Binder.restoreCallingIdentity(origId); return; }
// Log crash in battery stats. if (r != null) { mBatteryStatsService.noteProcessCrash(r.processName, r.uid); }
// 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; }
Message msg = Message.obtain(); msg.what = SHOW_ERROR_MSG; HashMap data = new HashMap(); data.put("result", result); data.put("app", r); msg.obj = data; mUiHandler.sendMessage(msg);
Binder.restoreCallingIdentity(origId); }
int res = result.get();
Intent appErrorIntent = null; synchronized (this) { if (r != null && !r.isolated) { // XXX Can't keep track of crash time for isolated processes, // since they don't have a persistent identity. mProcessCrashTimes.put(r.info.processName, r.uid, SystemClock.uptimeMillis()); } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); } }