aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/nullwire
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2009-06-10 08:27:58 +0000
committerKenny Root <kenny@the-b.org>2009-06-10 08:27:58 +0000
commitd62e6eebae78c8d59c46517fd5cea9be6942f5ba (patch)
tree620ce7a5e365d71097bb839fcfa2f81890a6a234 /src/com/nullwire
parente7e79db923cd98bfd804ae2dc8383d949b5ddddb (diff)
downloadconnectbot-d62e6eebae78c8d59c46517fd5cea9be6942f5ba.tar.gz
connectbot-d62e6eebae78c8d59c46517fd5cea9be6942f5ba.tar.bz2
connectbot-d62e6eebae78c8d59c46517fd5cea9be6942f5ba.zip
Add stack trace reporting
* When an unhandled exception occurs, the exception handler writes it to a file in addition to all ther other stuff that normally happens * The next time ConnectBot is started up, the user is prompted as to whether they want to submit the stack trace to aid in finding bugs. * If the user agrees or declines, the stack traces are deleted. git-svn-id: https://connectbot.googlecode.com/svn/trunk/connectbot@277 df292f66-193f-0410-a5fc-6d59da041ff2
Diffstat (limited to 'src/com/nullwire')
-rw-r--r--src/com/nullwire/trace/DefaultExceptionHandler.java46
-rw-r--r--src/com/nullwire/trace/ExceptionClickListener.java44
-rw-r--r--src/com/nullwire/trace/ExceptionHandler.java191
-rw-r--r--src/com/nullwire/trace/G.java11
4 files changed, 292 insertions, 0 deletions
diff --git a/src/com/nullwire/trace/DefaultExceptionHandler.java b/src/com/nullwire/trace/DefaultExceptionHandler.java
new file mode 100644
index 0000000..e475888
--- /dev/null
+++ b/src/com/nullwire/trace/DefaultExceptionHandler.java
@@ -0,0 +1,46 @@
+package com.nullwire.trace;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Random;
+
+import android.util.Log;
+
+public class DefaultExceptionHandler implements UncaughtExceptionHandler {
+
+ private static final String TAG = "UNHANDLED_EXCEPTION";
+
+ // Default exception handler
+ public void uncaughtException(Thread t, Throwable e) {
+ // Here you should have a more robust, permanent record of problems
+ final Writer result = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(result);
+ e.printStackTrace(printWriter);
+ try {
+ // Random number to avoid duplicate files
+ Random generator = new Random();
+ int random = generator.nextInt(99999);
+ // Embed version in stacktrace filename
+ String filename = G.APP_VERSION + "-" + Integer.toString(random);
+ Log.d(TAG, "Writing unhandled exception to: " + G.FILES_PATH + "/"
+ + filename + ".stacktrace");
+ // Write the stacktrace to disk
+ BufferedWriter bos = new BufferedWriter(new FileWriter(G.FILES_PATH
+ + "/" + filename + ".stacktrace"));
+ bos.write(result.toString());
+ bos.flush();
+ // Close up everything
+ bos.close();
+ } catch (Exception ebos) {
+ // Nothing much we can do about this - the game is over
+ ebos.printStackTrace();
+ }
+ Log.d(TAG, result.toString());
+ // FlurryAgent session has ended
+ t.getThreadGroup().destroy();
+ }
+}
diff --git a/src/com/nullwire/trace/ExceptionClickListener.java b/src/com/nullwire/trace/ExceptionClickListener.java
new file mode 100644
index 0000000..2c6e561
--- /dev/null
+++ b/src/com/nullwire/trace/ExceptionClickListener.java
@@ -0,0 +1,44 @@
+/**
+ *
+ */
+package com.nullwire.trace;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.util.Log;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class ExceptionClickListener implements OnClickListener {
+ public static String TAG = "com.nullwire.trace.ExceptionClickListener";
+
+ Context context;
+
+ public ExceptionClickListener(Context context) {
+ this.context = context;
+ }
+
+ public void onClick(DialogInterface dialog, int whichButton) {
+ switch (whichButton) {
+ case DialogInterface.BUTTON_POSITIVE:
+ Log.d(TAG, "Trying to submit stack traces");
+ new Thread(new Runnable() {
+ public void run() {
+ ExceptionHandler.submitStackTraces(context);
+ }
+ }).run();
+ dialog.dismiss();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ ExceptionHandler.removeStackTraces(context);
+ dialog.dismiss();
+ break;
+ default:
+ Log.d("ExceptionClickListener", "Got unknown button click: " + whichButton);
+ dialog.cancel();
+ }
+ }
+}
diff --git a/src/com/nullwire/trace/ExceptionHandler.java b/src/com/nullwire/trace/ExceptionHandler.java
new file mode 100644
index 0000000..b73ea86
--- /dev/null
+++ b/src/com/nullwire/trace/ExceptionHandler.java
@@ -0,0 +1,191 @@
+package com.nullwire.trace;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HTTP;
+import org.connectbot.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+public class ExceptionHandler {
+
+ public static String TAG = "com.nullwire.trace.ExceptionsHandler";
+
+ private static String[] stackTraceFileList = null;
+
+ private static AlertDialog submitDialog;
+
+ /**
+ * @param context
+ */
+ public static void checkForTraces(Context context) {
+ if (searchForStackTraces(context).length > 0) {
+ Log.d(TAG, "number of stack traces: " + searchForStackTraces(context).length);
+ askUserToSubmitExceptions(context);
+ }
+ }
+
+ /**
+ * @param context
+ */
+ private static void getPackageInfo(Context context) {
+ // Get information about the Package
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo pi;
+ // Version
+ pi = pm.getPackageInfo(context.getPackageName(), 0);
+ G.APP_VERSION = pi.versionName;
+ // Package name
+ G.APP_PACKAGE = pi.packageName;
+ // Files dir for storing the stack traces
+ G.FILES_PATH = context.getFilesDir().getAbsolutePath();
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ Log.d(TAG, "APP_VERSION: " + G.APP_VERSION);
+ Log.d(TAG, "APP_PACKAGE: " + G.APP_PACKAGE);
+ Log.d(TAG, "FILES_PATH: " + G.FILES_PATH);
+ Log.d(TAG, "URL: " + G.URL);
+ }
+
+ public static void register(Context context) {
+ Log.i(TAG, "Registering default exceptions handler");
+
+ new Thread() {
+ @Override
+ public void run() {
+ // Register default exceptions handler
+ Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler());
+ }
+ }.start();
+ }
+
+ /**
+ * Search for stack trace files.
+ *
+ * @return
+ */
+ private static String[] searchForStackTraces(Context context) {
+ if (stackTraceFileList != null && stackTraceFileList.length > 0)
+ return stackTraceFileList;
+
+ if (G.FILES_PATH == null)
+ getPackageInfo(context);
+
+ File dir = new File(G.FILES_PATH + "/");
+ // Try to create the files folder if it doesn't exist
+ dir.mkdir();
+ // Filter for ".stacktrace" files
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".stacktrace");
+ }
+ };
+ return (stackTraceFileList = dir.list(filter));
+ }
+
+ public static void askUserToSubmitExceptions(Context context) {
+ if (submitDialog == null) {
+ DialogInterface.OnClickListener clickListener = new ExceptionClickListener(context);
+ submitDialog = new AlertDialog.Builder(context)
+ .setMessage(R.string.exceptions_submit_message)
+ .setPositiveButton(android.R.string.ok, clickListener)
+ .setNegativeButton(android.R.string.no, clickListener)
+ .create();
+ }
+
+ submitDialog.show();
+ }
+
+ /**
+ * Look into the files folder to see if there are any "*.stacktrace" files.
+ * If any are present, submit them to the trace server.
+ */
+ public static void submitStackTraces(Context context) {
+ try {
+ Log.d(TAG, "Looking for exceptions in: " + G.FILES_PATH);
+ String[] list = searchForStackTraces(context);
+ if (list != null && list.length > 0) {
+ Log.d(TAG, "Found " + list.length + " stacktrace(s)");
+ StringBuilder contents = new StringBuilder();
+ for (int i = 0; i < list.length; i++) {
+ String filePath = G.FILES_PATH + "/" + list[i];
+ // Extract the version from the filename:
+ // "packagename-version-...."
+ String version = list[i].split("-")[0];
+ Log.d(TAG, "Stacktrace in file '" + filePath
+ + "' belongs to version " + version);
+ // Read contents of stacktrace
+ contents.setLength(0);
+ BufferedReader input;
+ try {
+ input = new BufferedReader(new FileReader(filePath));
+ } catch (FileNotFoundException fnf) {
+ continue;
+ }
+ String line = null;
+ while ((line = input.readLine()) != null) {
+ contents.append(line);
+ contents.append(System.getProperty("line.separator"));
+ }
+ input.close();
+ String stacktrace;
+ stacktrace = contents.toString();
+ Log.d(TAG, "Transmitting stack trace: " + stacktrace);
+ // Transmit stack trace with POST request
+ DefaultHttpClient httpClient = new DefaultHttpClient();
+ HttpPost httpPost = new HttpPost(G.URL);
+ List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+ nvps.add(new BasicNameValuePair("package_name", G.APP_PACKAGE));
+ nvps.add(new BasicNameValuePair("package_version", version));
+ nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
+ httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
+ // We don't care about the response, so we just hope it went
+ // well and on with it
+ httpClient.execute(httpPost);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ removeStackTraces(context);
+ }
+ }
+
+ public synchronized static void removeStackTraces(Context context) {
+ try {
+ String[] list = searchForStackTraces(context);
+
+ if (list == null)
+ return;
+
+ for (int i = 0; i < list.length; i++) {
+ File file = new File(G.FILES_PATH + "/" + list[i]);
+ file.delete();
+ }
+
+ list = null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/com/nullwire/trace/G.java b/src/com/nullwire/trace/G.java
new file mode 100644
index 0000000..d803a05
--- /dev/null
+++ b/src/com/nullwire/trace/G.java
@@ -0,0 +1,11 @@
+package com.nullwire.trace;
+
+public class G {
+ // This must be set by the application - it used to automatically
+ // transmit exceptions to the trace server
+ public static String FILES_PATH = null;
+ public static String APP_VERSION = "unknown";
+ public static String APP_PACKAGE = "unknown";
+ // Where are the stack traces posted?
+ public static String URL = "http://connectbot.the-b.org/trace/";
+}