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 11package org.appspot.apprtc; 12 13import android.app.Activity; 14import android.app.AlertDialog; 15import android.content.DialogInterface; 16import android.util.Log; 17import android.util.TypedValue; 18import android.widget.ScrollView; 19import android.widget.TextView; 20 21import java.io.PrintWriter; 22import 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 */ 32public class UnhandledExceptionHandler 33 implements Thread.UncaughtExceptionHandler { 34 private static final String TAG = "AppRTCDemoActivity"; 35 private final Activity activity; 36 37 public UnhandledExceptionHandler(final Activity activity) { 38 this.activity = activity; 39 } 40 41 public void uncaughtException(Thread unusedThread, final Throwable e) { 42 activity.runOnUiThread(new Runnable() { 43 @Override public void run() { 44 String title = "Fatal error: " + getTopLevelCauseMessage(e); 45 String msg = getRecursiveStackTrace(e); 46 TextView errorView = new TextView(activity); 47 errorView.setText(msg); 48 errorView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 8); 49 ScrollView scrollingContainer = new ScrollView(activity); 50 scrollingContainer.addView(errorView); 51 Log.e(TAG, title + "\n\n" + msg); 52 DialogInterface.OnClickListener listener = 53 new DialogInterface.OnClickListener() { 54 @Override public void onClick( 55 DialogInterface dialog, int which) { 56 dialog.dismiss(); 57 System.exit(1); 58 } 59 }; 60 AlertDialog.Builder builder = 61 new AlertDialog.Builder(activity); 62 builder 63 .setTitle(title) 64 .setView(scrollingContainer) 65 .setPositiveButton("Exit", listener).show(); 66 } 67 }); 68 } 69 70 // Returns the Message attached to the original Cause of |t|. 71 private static String getTopLevelCauseMessage(Throwable t) { 72 Throwable topLevelCause = t; 73 while (topLevelCause.getCause() != null) { 74 topLevelCause = topLevelCause.getCause(); 75 } 76 return topLevelCause.getMessage(); 77 } 78 79 // Returns a human-readable String of the stacktrace in |t|, recursively 80 // through all Causes that led to |t|. 81 private static String getRecursiveStackTrace(Throwable t) { 82 StringWriter writer = new StringWriter(); 83 t.printStackTrace(new PrintWriter(writer)); 84 return writer.toString(); 85 } 86} 87