Fix exception handling in snippets. (#32)

* Don't try to call methods on snippet classes that couldn't be constructed
* Don't obscure the cause of exceptions
* Propagate the entire stack trace to the python side
* Decorate the stacktrace
diff --git a/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example1/ExampleSnippet2.java b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example1/ExampleSnippet2.java
index 43c3db8..3077869 100644
--- a/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example1/ExampleSnippet2.java
+++ b/examples/ex1_standalone_app/src/main/java/com/google/android/mobly/snippet/example1/ExampleSnippet2.java
@@ -18,6 +18,7 @@
 
 import com.google.android.mobly.snippet.Snippet;
 import com.google.android.mobly.snippet.rpc.Rpc;
+import java.io.IOException;
 
 public class ExampleSnippet2 implements Snippet {
     @Rpc(description = "Returns the given string with the prefix \"bar\"")
@@ -25,6 +26,11 @@
         return "bar " + input;
     }
 
+    @Rpc(description = "Throws an exception")
+    public String throwSomething() throws IOException {
+        throw new IOException("Example exception from throwSomething()");
+    }
+
     @Override
     public void shutdown() {}
 }
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
index edb872b..e27c473 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
@@ -19,6 +19,7 @@
 import android.os.Build;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.HashMap;
@@ -63,7 +64,7 @@
     }
 
     public Object invoke(Class<? extends Snippet> clazz, Method method, Object[] args)
-            throws Exception {
+            throws Throwable {
         if (method.isAnnotationPresent(RpcMinSdk.class)) {
             int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value();
             if (Build.VERSION.SDK_INT < requiredSdkLevel) {
@@ -72,8 +73,12 @@
                                 method.getName(), requiredSdkLevel, Build.VERSION.SDK_INT));
             }
         }
-        Snippet object = get(clazz);
-        return method.invoke(object, args);
+        try {
+            Snippet object = get(clazz);
+            return method.invoke(object, args);
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
     }
 
     public void shutdown() {
@@ -88,21 +93,16 @@
         }
     }
 
-    private Snippet get(Class<? extends Snippet> clazz) {
+    private Snippet get(Class<? extends Snippet> clazz) throws Exception {
         Snippet object = mReceivers.get(clazz);
         if (object != null) {
             return object;
         }
 
         Constructor<? extends Snippet> constructor;
-        try {
-            constructor = clazz.getConstructor();
-            object = constructor.newInstance();
-            mReceivers.put(clazz, object);
-        } catch (Exception e) {
-            Log.e(e);
-        }
-
+        constructor = clazz.getConstructor();
+        object = constructor.newInstance();
+        mReceivers.put(clazz, object);
         return object;
     }
 }
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
index 0c1d0f2..ce02344 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
@@ -16,6 +16,8 @@
 
 package com.google.android.mobly.snippet.rpc;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -48,10 +50,16 @@
   }
 
   public static JSONObject error(int id, Throwable t) throws JSONException {
+    StringWriter stackTraceWriter = new StringWriter();
+    stackTraceWriter.write("\n-------------- Java Stacktrace ---------------\n");
+    t.printStackTrace(new PrintWriter(stackTraceWriter));
+    stackTraceWriter.write("----------------------------------------------");
+    String stackTrace = stackTraceWriter.toString();
+
     JSONObject json = new JSONObject();
     json.put("id", id);
     json.put("result", JSONObject.NULL);
-    json.put("error", t.toString());
+    json.put("error", stackTrace);
     return json;
   }
 }
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/MethodDescriptor.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/MethodDescriptor.java
index 8d18582..862278e 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/MethodDescriptor.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/MethodDescriptor.java
@@ -88,17 +88,7 @@
       }
     }
 
-    return invoke(manager, args);
-  }
-
-  private Object invoke(SnippetManager manager, Object[] args) throws Throwable {
-    Object result;
-    try {
-      result = manager.invoke(mClass, mMethod, args);
-    } catch (Throwable t) {
-      throw t.getCause();
-    }
-    return result;
+    return manager.invoke(mClass, mMethod, args);
   }
 
   /**