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
33     implements Thread.UncaughtExceptionHandler {
34   private static final String TAG = "AppRTCDemoActivity";
35   private final Activity activity;
36 
UnhandledExceptionHandler(final Activity activity)37   public UnhandledExceptionHandler(final Activity activity) {
38     this.activity = activity;
39   }
40 
uncaughtException(Thread unusedThread, final Throwable e)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|.
getTopLevelCauseMessage(Throwable 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|.
getRecursiveStackTrace(Throwable 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