aboutsummaryrefslogtreecommitdiff
path: root/third_party/sl4a
diff options
context:
space:
mode:
authorAlexander Dorokhine <adorokhine@google.com>2017-05-24 16:28:22 -0700
committerGitHub <noreply@github.com>2017-05-24 16:28:22 -0700
commit110f5c76b325b6ce71cc606966f6fa6a3583b140 (patch)
tree5c02ece6871a402a78ae5169c56badfda8560e84 /third_party/sl4a
parent0f203a5006a21abd05a2d7ca5b29d18aac125cb9 (diff)
downloadmobly-snippet-lib-110f5c76b325b6ce71cc606966f6fa6a3583b140.tar.gz
When launching snippet, print protocol version and port to output. (#59)
Current messages are: `SNIPPET START, PROTOCOL <major> <minor>` immediately once snippet starts `SERVING <port id>` once the server has been successfully launched This allows us to: - Know that a snippet launched (versus crashing immediately due to e.g. classpath issues, so we don't need to bother trying to connect to a dead snippet - Know what version of startup and communication protocol it's using so we can configure the client appropriately and manage compatibility. - Allow the device side to choose a port number, to avoid the client side having to pick a port number for the snippet and hoping it's free. This allows us to manage backwards compatibility and device-side port allocation. Fixes #10. Fixes #62.
Diffstat (limited to 'third_party/sl4a')
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/SnippetRunner.java69
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java4
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/SimpleServer.java6
3 files changed, 68 insertions, 11 deletions
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/SnippetRunner.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/SnippetRunner.java
index d6ed8f4..081d66e 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/SnippetRunner.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/SnippetRunner.java
@@ -15,6 +15,7 @@
*/
package com.google.android.mobly.snippet;
+import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
@@ -36,8 +37,45 @@ import java.net.SocketException;
* snippets are launched with 'am instrument'. We're specifically extending {@link
* AndroidJUnitRunner} because Espresso requires being called through it, since it sets up {@link
* android.support.test.InstrumentationRegistry} which Espresso requires.
+ *
+ * <p>The launch and communication protocol between snippet and client is versionated and reported
+ * as follows:
+ *
+ * <ul>
+ * <li>v0 (not reported):
+ * <ul>
+ * <li>Launch as Instrumentation with SnippetRunner.
+ * <li>No protocol-specific messages reported through instrumentation output.
+ * <li>'stop' action prints 'OK (0 tests)'
+ * <li>'start' action prints nothing.
+ * </ul>
+ *
+ * <li>v1.0: New instrumentation output added to track bringup process
+ * <ul>
+ * <li>"SNIPPET START, PROTOCOL &lt;major&gt; &lt;minor&gt;" upon snippet start
+ * <li>"SNIPPET SERVING, PORT &lt;port&gt;" once server is ready
+ * </ul>
+ *
+ * </ul>
*/
public class SnippetRunner extends AndroidJUnitRunner {
+
+ /**
+ * Major version of the launch and communication protocol.
+ *
+ * <p>Incrementing this means that compatibility with clients using the older version is broken.
+ * Avoid breaking compatibility unless there is no other choice.
+ */
+ public static final int PROTOCOL_MAJOR_VERSION = 1;
+
+ /**
+ * Minor version of the launch and communication protocol.
+ *
+ * <p>Increment this when new features are added to the launch and communication protocol that
+ * are backwards compatible with the old protocol and don't break existing clients.
+ */
+ public static final int PROTOCOL_MINOR_VERSION = 0;
+
private static final String ARG_ACTION = "action";
private static final String ARG_PORT = "port";
@@ -59,6 +97,10 @@ public class SnippetRunner extends AndroidJUnitRunner {
// First-run static setup
Log.initLogTag(getContext());
+ // First order of business is to report HELLO to instrumentation output.
+ sendString(
+ "SNIPPET START, PROTOCOL " + PROTOCOL_MAJOR_VERSION + " " + PROTOCOL_MINOR_VERSION);
+
// Prevent this runner from triggering any real JUnit tests in the snippet by feeding it a
// hardcoded empty test class.
mArguments.putString("class", EmptyTestClass.class.getCanonicalName());
@@ -78,10 +120,10 @@ public class SnippetRunner extends AndroidJUnitRunner {
switch (action) {
case START:
String servicePort = mArguments.getString(ARG_PORT);
- if (servicePort == null) {
- throw new IllegalArgumentException("\"--e port <port>\" was not specified");
+ int port = 0 /* auto chosen */;
+ if (servicePort != null) {
+ port = Integer.parseInt(servicePort);
}
- int port = Integer.parseInt(servicePort);
startServer(port);
break;
case STOP:
@@ -98,18 +140,18 @@ public class SnippetRunner extends AndroidJUnitRunner {
} catch (SocketException e) {
if (e.getMessage().equals("Permission denied")) {
throw new RuntimeException(
- "Failed to start server on port "
- + port
- + ". No permission to create a socket. Does the *MAIN* app manifest "
- + "declare the INTERNET permission?",
+ "Failed to start server. No permission to create a socket. Does the *MAIN* "
+ + "app manifest declare the INTERNET permission?",
e);
}
- throw new RuntimeException("Failed to start server on port " + port, e);
+ throw new RuntimeException("Failed to start server", e);
} catch (IOException e) {
- throw new RuntimeException("Failed to start server on port " + port, e);
+ throw new RuntimeException("Failed to start server", e);
}
createNotification();
- Log.i("Snippet server started for process " + Process.myPid() + " on port " + port);
+ int actualPort = androidProxy.getPort();
+ sendString("SNIPPET SERVING, PORT " + actualPort);
+ Log.i("Snippet server started for process " + Process.myPid() + " on port " + actualPort);
}
private void createNotification() {
@@ -122,4 +164,11 @@ public class SnippetRunner extends AndroidJUnitRunner {
mNotification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
}
+
+ private void sendString(String string) {
+ Log.i("Sending protocol message: " + string);
+ Bundle bundle = new Bundle();
+ bundle.putString(Instrumentation.REPORT_KEY_STREAMRESULT, string + "\n");
+ sendStatus(0, bundle);
+ }
}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
index 6434814..cb2e772 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
@@ -34,4 +34,8 @@ public class AndroidProxy {
public void startLocal(int port) throws IOException {
mJsonRpcServer.startLocal(port);
}
+
+ public int getPort() {
+ return mJsonRpcServer.getPort();
+ }
}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/SimpleServer.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/SimpleServer.java
index 4a87386..8d86723 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/SimpleServer.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/SimpleServer.java
@@ -192,10 +192,14 @@ public abstract class SimpleServer {
*/
public void startLocal(int port) throws IOException {
InetAddress address = getPrivateInetAddress();
- mServer = new ServerSocket(port, 5, address);
+ mServer = new ServerSocket(port, 5 /* backlog */, address);
start();
}
+ public int getPort() {
+ return mServer.getLocalPort();
+ }
+
private void start() {
mServerThread =
new Thread() {