diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/nullwire/trace/DefaultExceptionHandler.java | 46 | ||||
-rw-r--r-- | src/com/nullwire/trace/ExceptionClickListener.java | 44 | ||||
-rw-r--r-- | src/com/nullwire/trace/ExceptionHandler.java | 191 | ||||
-rw-r--r-- | src/com/nullwire/trace/G.java | 11 | ||||
-rw-r--r-- | src/org/connectbot/ConsoleActivity.java | 13 | ||||
-rw-r--r-- | src/org/connectbot/HostListActivity.java | 12 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalManager.java | 5 |
7 files changed, 321 insertions, 1 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/"; +} diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index a88c6d0..20673eb 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -67,6 +67,9 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewFlipper; + +import com.nullwire.trace.ExceptionHandler; + import de.mud.terminal.vt320; public class ConsoleActivity extends Activity { @@ -294,6 +297,8 @@ public class ConsoleActivity extends Activity { this.setContentView(R.layout.act_console); + ExceptionHandler.register(this); + clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); prefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -715,7 +720,6 @@ public class ConsoleActivity extends Activity { // this also keeps the wifi chipset from disconnecting us if(wakelock != null && prefs.getBoolean(PreferenceConstants.KEEP_ALIVE, true)) wakelock.acquire(); - } @Override @@ -729,6 +733,13 @@ public class ConsoleActivity extends Activity { wakelock.release(); } + @Override + public void onResume() { + super.onResume(); + + ExceptionHandler.checkForTraces(this); + } + protected void shiftLeft() { View overlay; boolean shouldAnimate = flip.getChildCount() > 1; diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index 662da65..17edd0f 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -64,6 +64,8 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; +import com.nullwire.trace.ExceptionHandler; + public class HostListActivity extends ListActivity { protected TerminalManager bound = null; @@ -110,6 +112,7 @@ public class HostListActivity extends ListActivity { this.updateList(); + ExceptionHandler.checkForTraces(this); } @Override @@ -124,6 +127,13 @@ public class HostListActivity extends ListActivity { } + @Override + public void onResume() { + super.onResume(); + + ExceptionHandler.checkForTraces(this); + } + public final static int REQUEST_EDIT = 1; public final static int REQUEST_EULA = 2; @@ -157,6 +167,8 @@ public class HostListActivity extends ListActivity { getResources().getText(R.string.app_name), getResources().getText(R.string.title_hosts_list))); + ExceptionHandler.register(this); + // check for eula agreement this.prefs = PreferenceManager.getDefaultSharedPreferences(this); diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index 9719452..27812d8 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -61,6 +61,8 @@ import android.os.Vibrator; import android.preference.PreferenceManager; import android.util.Log; +import com.nullwire.trace.ExceptionHandler; + /** * Manager for SSH connections that runs as a background service. This service * holds a list of currently connected SSH bridges that are ready for connection @@ -110,6 +112,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen @Override public void onCreate() { Log.i(TAG, "Starting background service"); + + ExceptionHandler.register(this); + prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener(this); |