1 /* 2 * Copyright 2013 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.appspot.apprtc; 12 13 import android.app.Activity; 14 import android.app.AlertDialog; 15 import android.content.DialogInterface; 16 import android.util.Log; 17 import android.util.TypedValue; 18 import android.widget.ScrollView; 19 import android.widget.TextView; 20 21 import java.io.PrintWriter; 22 import java.io.StringWriter; 23 24 /** 25 * Singleton helper: install a default unhandled exception handler which shows 26 * an informative dialog and kills the app. Useful for apps whose 27 * error-handling consists of throwing RuntimeExceptions. 28 * NOTE: almost always more useful to 29 * Thread.setDefaultUncaughtExceptionHandler() rather than 30 * Thread.setUncaughtExceptionHandler(), to apply to background threads as well. 31 */ 32 public class UnhandledExceptionHandler implements Thread.UncaughtExceptionHandler { 33 private static final String TAG = "AppRTCMobileActivity"; 34 private final Activity activity; 35 UnhandledExceptionHandler(final Activity activity)36 public UnhandledExceptionHandler(final Activity activity) { 37 this.activity = activity; 38 } 39 40 @Override uncaughtException(Thread unusedThread, final Throwable e)41 public void uncaughtException(Thread unusedThread, final Throwable e) { 42 activity.runOnUiThread(new Runnable() { 43 @Override 44 public void run() { 45 String title = "Fatal error: " + getTopLevelCauseMessage(e); 46 String msg = getRecursiveStackTrace(e); 47 TextView errorView = new TextView(activity); 48 errorView.setText(msg); 49 errorView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 8); 50 ScrollView scrollingContainer = new ScrollView(activity); 51 scrollingContainer.addView(errorView); 52 Log.e(TAG, title + "\n\n" + msg); 53 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { 54 @Override 55 public void onClick(DialogInterface dialog, int which) { 56 dialog.dismiss(); 57 System.exit(1); 58 } 59 }; 60 AlertDialog.Builder builder = new AlertDialog.Builder(activity); 61 builder.setTitle(title) 62 .setView(scrollingContainer) 63 .setPositiveButton("Exit", listener) 64 .show(); 65 } 66 }); 67 } 68 69 // Returns the Message attached to the original Cause of |t|. getTopLevelCauseMessage(Throwable t)70 private static String getTopLevelCauseMessage(Throwable t) { 71 Throwable topLevelCause = t; 72 while (topLevelCause.getCause() != null) { 73 topLevelCause = topLevelCause.getCause(); 74 } 75 return topLevelCause.getMessage(); 76 } 77 78 // Returns a human-readable String of the stacktrace in |t|, recursively 79 // through all Causes that led to |t|. getRecursiveStackTrace(Throwable t)80 private static String getRecursiveStackTrace(Throwable t) { 81 StringWriter writer = new StringWriter(); 82 t.printStackTrace(new PrintWriter(writer)); 83 return writer.toString(); 84 } 85 } 86