diff options
author | Alexander Dorokhine <adorokhine@google.com> | 2017-05-24 16:28:22 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-24 16:28:22 -0700 |
commit | 110f5c76b325b6ce71cc606966f6fa6a3583b140 (patch) | |
tree | 5c02ece6871a402a78ae5169c56badfda8560e84 /third_party/sl4a | |
parent | 0f203a5006a21abd05a2d7ca5b29d18aac125cb9 (diff) | |
download | mobly-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')
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 <major> <minor>" upon snippet start + * <li>"SNIPPET SERVING, PORT <port>" 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() { |