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