aboutsummaryrefslogtreecommitdiff
path: root/android/WALT/app/src/main
diff options
context:
space:
mode:
authorAndrew Lehmer <alehmer@google.com>2017-05-09 10:33:40 -0700
committerAndrew Lehmer <alehmer@google.com>2017-05-09 10:33:40 -0700
commit2d3f8a1b548faefee8484ad5dcf5cfc698bcd443 (patch)
treea30a23f07caa231d5014329963d8af7b07f41ab8 /android/WALT/app/src/main
parented462e937cd99e9aaee98613c4e3e0469145d719 (diff)
downloadwalt-2d3f8a1b548faefee8484ad5dcf5cfc698bcd443.tar.gz
walt: modify for use with Salad Fingersoreo-mr1-devoreo-dr1-dev
Bug: 37779957 Test: end-to-end tests with uncommited PTS changes for salad_fingers Change-Id: I4d0a59b04fb92d45899400ca36eb93fb8249c086
Diffstat (limited to 'android/WALT/app/src/main')
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java13
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java93
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/RemoteClockInfo.java68
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/RobotAutomationListener.java27
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java22
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java17
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java37
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/WaltTcpConnection.java46
8 files changed, 227 insertions, 96 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java
index 109fcf8..af03e36 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java
@@ -39,7 +39,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
-public class DragLatencyFragment extends Fragment implements View.OnClickListener {
+public class DragLatencyFragment extends Fragment
+ implements View.OnClickListener, RobotAutomationListener {
private SimpleLogger logger;
private WaltDevice waltDevice;
@@ -332,6 +333,16 @@ public class DragLatencyFragment extends Fragment implements View.OnClickListene
}
}
+ public void onRobotAutomationEvent(String event) {
+ if (event.equals(RobotAutomationListener.RESTART_EVENT)) {
+ onClick(restartButton);
+ } else if (event.equals(RobotAutomationListener.START_EVENT)) {
+ onClick(startButton);
+ } else if (event.equals(RobotAutomationListener.FINISH_EVENT)) {
+ onClick(finishButton);
+ }
+ }
+
private WaltDevice.TriggerHandler triggerHandler = new WaltDevice.TriggerHandler() {
@Override
public void onReceive(WaltDevice.TriggerMessage tmsg) {
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java
index 7efee00..ac1df47 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java
@@ -65,6 +65,10 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "WALT";
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_SHARE_LOG = 2;
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_SYSTRACE = 3;
+ private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_WRITE_LOG = 4;
+ private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_CLEAR_LOG = 5;
+
+ private static final String LOG_FILENAME = "qstep_log.txt";
private Toolbar toolbar;
LocalBroadcastManager broadcastManager;
@@ -74,6 +78,8 @@ public class MainActivity extends AppCompatActivity {
public Handler handler = new Handler();
+ private Fragment mRobotAutomationFragment;
+
/**
* A method to display exceptions on screen. This is very useful because our USB port is taken
@@ -132,6 +138,41 @@ public class MainActivity extends AppCompatActivity {
autoRunFragment.setArguments(intent.getExtras());
switchScreen(autoRunFragment, "Automated Test");
}
+
+ // Handle robot automation originating from adb shell am
+ if (intent != null && Intent.ACTION_SEND.equals(intent.getAction())) {
+ Log.e(TAG, "Received Intent: " + intent.toString());
+ String test = intent.getStringExtra("StartTest");
+ if (test != null) {
+ Log.e(TAG, "Extras \"StartTest\" = " + test);
+ if ("TapLatencyTest".equals(test)) {
+ mRobotAutomationFragment = new TapLatencyFragment();
+ switchScreen(mRobotAutomationFragment, "Tap Latency");
+ } else if ("ScreenResponseTest".equals(test)) {
+ mRobotAutomationFragment = new ScreenResponseFragment();
+ switchScreen(mRobotAutomationFragment, "Screen Response");
+ } else if ("DragLatencyTest".equals(test)) {
+ mRobotAutomationFragment = new DragLatencyFragment();
+ switchScreen(mRobotAutomationFragment, "Drag Latency");
+ }
+ }
+
+ String robotEvent = intent.getStringExtra("RobotAutomationEvent");
+ if (robotEvent != null && mRobotAutomationFragment != null) {
+ Log.e(TAG, "Received robot automation event=\"" + robotEvent + "\", Fragment = " +
+ mRobotAutomationFragment);
+ // Writing and clearing the log is not fragment-specific, so handle them here.
+ if (robotEvent.equals(RobotAutomationListener.WRITE_LOG_EVENT)) {
+ attemptSaveLog();
+ } else if (robotEvent.equals(RobotAutomationListener.CLEAR_LOG_EVENT)) {
+ attemptClearLog();
+ } else {
+ // All other robot automation events are forwarded to the current fragment.
+ ((RobotAutomationListener) mRobotAutomationFragment)
+ .onRobotAutomationEvent(robotEvent);
+ }
+ }
+ }
}
@Override
@@ -389,6 +430,30 @@ public class MainActivity extends AppCompatActivity {
}
}
+ private void attemptSaveLog() {
+ int currentPermission = ContextCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (currentPermission == PackageManager.PERMISSION_GRANTED) {
+ saveLogToFile();
+ } else {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_WRITE_LOG);
+ }
+ }
+
+ private void attemptClearLog() {
+ int currentPermission = ContextCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (currentPermission == PackageManager.PERMISSION_GRANTED) {
+ clearLogFile();
+ } else {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_CLEAR_LOG);
+ }
+ }
+
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
@@ -401,6 +466,12 @@ public class MainActivity extends AppCompatActivity {
case PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_SHARE_LOG:
attemptSaveAndShareLog();
break;
+ case PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_WRITE_LOG:
+ attemptSaveLog();
+ break;
+ case PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE_CLEAR_LOG:
+ attemptClearLog();
+ break;
}
}
@@ -414,22 +485,19 @@ public class MainActivity extends AppCompatActivity {
// is frowned upon, but deliberately giving permissions as part of the intent is
// way too cumbersome.
- String fname = "qstep_log.txt";
// A reasonable world readable location,on many phones it's /storage/emulated/Documents
// TODO: make this location configurable?
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File file = null;
FileOutputStream outStream = null;
- Date now = new Date();
- logger.log("Saving log to:\n" + path.getPath() + "/" + fname);
- logger.log("On: " + now.toString());
-
try {
if (!path.exists()) {
path.mkdirs();
}
- file = new File(path, fname);
+ file = new File(path, LOG_FILENAME);
+ logger.log("Saving log to: " + file + " at " + new Date());
+
outStream = new FileOutputStream(file);
outStream.write(logger.getLogText().getBytes());
@@ -437,11 +505,22 @@ public class MainActivity extends AppCompatActivity {
logger.log("Log saved");
} catch (Exception e) {
e.printStackTrace();
- logger.log("Exception:\n" + e.getMessage());
+ logger.log("Failed to write log: " + e.getMessage());
}
return file.getPath();
}
+ public void clearLogFile() {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ try {
+ File file = new File(path, LOG_FILENAME);
+ file.delete();
+ } catch (Exception e) {
+ e.printStackTrace();
+ logger.log("Failed to clear log: " + e.getMessage());
+ }
+ }
+
public void shareLogFile(String filepath) {
File file = new File(filepath);
logger.log("Firing Intent.ACTION_SEND for file:");
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/RemoteClockInfo.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/RemoteClockInfo.java
index 1a42eb5..4db9358 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/RemoteClockInfo.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/RemoteClockInfo.java
@@ -16,10 +16,6 @@
package org.chromium.latency.walt;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-
/**
* Representation of our best knowledge of the remote clock.
* All time variables here are stored in microseconds.
@@ -27,18 +23,18 @@ import java.lang.reflect.Method;
* Which time reporting function is used locally on Android:
* This app uses SystemClock.uptimeMillis() for keeping local time which, up to
* units, is the same time reported by System.nanoTime() and by
- * clock_gettime(CLOCK_MONOTONIC, &ts) from time.h and is, roughly, the time
+ * clock_gettime(CLOCK_MONOTONIC, &amp;ts) from time.h and is, roughly, the time
* elapsed since last boot, excluding sleep time.
*
- * base_time is the local Android time when remote clock was zeroed.
+ * base_time is the local monotonic clock time when remote clock was zeroed.
*
* micros() is our best available approximation of the current reading of the remote clock.
*
- * Immediately after synchronization minLag is set to zero and the remote clock guaranteed to lag
- * behind what micros() reports by at most maxLag.
+ * Immediately after synchronization, minLag is set to zero and the remote clock is guaranteed to
+ * lag behind what micros() reports by at most maxLag.
*
* Immediately after synchronization or an update of the bounds (minLag, maxLag) the following holds
- * t_remote + minLag < micros() < t_rmote + maxLag
+ * t_remote + minLag &lt; micros() &lt; t_remote + maxLag
*
* For more details about clock synchronization refer to
* https://github.com/google/walt/blob/master/android/WALT/app/src/main/jni/README.md
@@ -50,67 +46,19 @@ public class RemoteClockInfo {
public int maxLag;
public long baseTime;
-
- public long micros() {
- return microTime() - baseTime;
- }
-
public static long microTime() {
return System.nanoTime() / 1000;
}
-
- /**
- Find the wall time when uptime was zero = CLOCK_REALTIME - CLOCK_MONOTONIC
-
- Needed for TCP bridge because Python prior to 3.3 has no direct access to CLOCK_MONOTONIC
- so the bridge returns timestamps as wall time and we need to convert them to CLOCK_MONOTONIC.
-
- See:
- [1] https://docs.python.org/3/library/time.html#time.CLOCK_MONOTONIC
- [2] http://stackoverflow.com/questions/14270300/what-is-the-difference-between-clock-monotonic-clock-monotonic-raw
- [3] http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python
-
- android.os.SystemClock.currentTimeMicros() is hidden by @hide which means it can't be called
- directly - calling it via reflection.
-
- See:
- http://stackoverflow.com/questions/17035271/what-does-hide-mean-in-the-android-source-code
- */
- public static long uptimeZero() {
- long t = -1;
- long dt = Long.MAX_VALUE;
- try {
- Class cls = Class.forName("android.os.SystemClock");
- Method myTimeGetter = cls.getMethod("currentTimeMicro");
- t = (long) myTimeGetter.invoke(null);
- dt = t - microTime();
- } catch (Exception e) {
- Log.i("WALT.uptimeZero", e.getMessage());
- }
-
- return dt;
- }
-
- public static long currentTimeMicro() {
-
- long t = -1;
- try {
- Class cls = Class.forName("android.os.SystemClock");
- Method myTimeGetter = cls.getMethod("currentTimeMicro");
- t = (long) myTimeGetter.invoke(null);
- } catch (Exception e) {
- Log.i("WALT.currentTimeMicro", e.getMessage());
- }
-
- return t;
+ public long micros() {
+ return microTime() - baseTime;
}
public int getMeanLag() {
return (minLag + maxLag) / 2;
}
- public String toString(){
+ public String toString() {
return "Remote clock [us]: current time = " + micros() + " baseTime = " + baseTime +
" lagBounds = (" + minLag + ", " + maxLag + ")";
}
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/RobotAutomationListener.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/RobotAutomationListener.java
new file mode 100644
index 0000000..47a21ba
--- /dev/null
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/RobotAutomationListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.chromium.latency.walt;
+
+public interface RobotAutomationListener {
+ public static final String START_EVENT = "START_EVENT";
+ public static final String RESTART_EVENT = "RESTART_EVENT";
+ public static final String FINISH_EVENT = "FINISH_EVENT";
+ public static final String WRITE_LOG_EVENT = "WRITE_LOG_EVENT";
+ public static final String CLEAR_LOG_EVENT = "CLEAR_LOG_EVENT";
+
+ public void onRobotAutomationEvent(String event);
+}
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java
index cfe6a53..629ed7d 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java
@@ -53,7 +53,8 @@ import static org.chromium.latency.walt.Utils.getIntPreference;
/**
* Measurement of screen response time when switching between black and white.
*/
-public class ScreenResponseFragment extends Fragment implements View.OnClickListener {
+public class ScreenResponseFragment extends Fragment
+ implements View.OnClickListener, RobotAutomationListener {
private static final int CURVE_TIMEOUT = 1000; // milliseconds
private static final int CURVE_BLINK_TIME = 250; // milliseconds
@@ -248,14 +249,13 @@ public class ScreenResponseFragment extends Fragment implements View.OnClickList
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
- // frameTimeNanos is he time in nanoseconds when the frame started being
+ // frameTimeNanos is the time in nanoseconds when the frame started being
// rendered, in the nanoTime() timebase.
lastFrameStartTime = frameTimeNanos / 1000 - waltDevice.clock.baseTime;
lastFrameCallbackTime = System.nanoTime() / 1000 - waltDevice.clock.baseTime;
}
});
-
// Repost doBlink to some far away time to blink again even if nothing arrives from
// Teensy. This callback will almost always get cancelled by onIncomingTimestamp()
handler.postDelayed(doBlinkRunnable, 550 + (long) (Math.random()*100));
@@ -290,9 +290,9 @@ public class ScreenResponseFragment extends Fragment implements View.OnClickList
}
}
- final long startTimeMicros = lastFrameStartTime + waltDevice.clock.baseTime;
- final long finishTimeMicros = tmsg.t + waltDevice.clock.baseTime;
if (traceLogger != null) {
+ final long startTimeMicros = lastFrameStartTime + waltDevice.clock.baseTime;
+ final long finishTimeMicros = tmsg.t + waltDevice.clock.baseTime;
traceLogger.log(startTimeMicros, finishTimeMicros,
isBoxWhite ? "Black-to-white" : "White-to-black",
"Bar starts at beginning of frame and ends when photosensor detects blink");
@@ -423,6 +423,18 @@ public class ScreenResponseFragment extends Fragment implements View.OnClickList
}
}
+ public void onRobotAutomationEvent(String event) {
+ // Never show the latency chart during automated runs.
+ shouldShowLatencyChart = false;
+ // Disable full-screen mode to prevent modal user dialog.
+ enableFullScreen = false;
+ if (event.equals(RobotAutomationListener.RESTART_EVENT)) {
+ onClick(stopButton);
+ } else if (event.equals(RobotAutomationListener.START_EVENT)) {
+ onClick(startButton);
+ }
+ }
+
private WaltDevice.TriggerHandler brightnessTriggerHandler = new WaltDevice.TriggerHandler() {
@Override
public void onReceive(WaltDevice.TriggerMessage tmsg) {
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
index 64e333d..e26a328 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
@@ -36,7 +36,7 @@ import java.util.Locale;
import static org.chromium.latency.walt.Utils.getBooleanPreference;
public class TapLatencyFragment extends Fragment
- implements View.OnClickListener {
+ implements View.OnClickListener, RobotAutomationListener {
private static final int ACTION_DOWN_INDEX = 0;
private static final int ACTION_UP_INDEX = 1;
@@ -193,8 +193,8 @@ public class TapLatencyFragment extends Fragment
}
if (dt < 0 || dt > 200) {
- logger.log(action + " bogus kernelTime, ignored, dt=" + dt);
- return false;
+ logger.log(action + " bogus kernelTime=" + e.kernelTime + ", ignored, dt=" + dt);
+ return false;
}
return true;
}
@@ -303,4 +303,15 @@ public class TapLatencyFragment extends Fragment
}
}
+
+ public void onRobotAutomationEvent(String event) {
+ // Never show the latency chart during automated runs.
+ shouldShowLatencyChart = false;
+ if (event.equals(RobotAutomationListener.RESTART_EVENT) ||
+ event.equals(RobotAutomationListener.START_EVENT)) {
+ restartMeasurement();
+ } else if (event.equals(RobotAutomationListener.FINISH_EVENT)) {
+ finishAndShowStats();
+ }
+ }
}
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java
index 96ddcfd..8ec2cb4 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java
@@ -156,16 +156,20 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener {
private String sendReceive(char c) throws IOException {
- connection.sendByte(c);
- return readOne();
+ synchronized (connection) {
+ connection.sendByte(c);
+ return readOne();
+ }
}
public void sendAndFlush(char c) {
try {
- connection.sendByte(c);
- while(connection.blockingRead(buffer) > 0) {
- // flushing all incoming data
+ synchronized (connection) {
+ connection.sendByte(c);
+ while (connection.blockingRead(buffer) > 0) {
+ // flushing all incoming data
+ }
}
} catch (Exception e) {
logger.log("Exception in sendAndFlush: " + e.getMessage());
@@ -187,6 +191,7 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener {
throw new IOException("Unexpected response from WALT. Expected \"" + ack
+ "\", got \"" + response + "\"");
}
+ // Trim out the ack
return response.substring(1).trim();
}
@@ -237,6 +242,10 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener {
return;
}
connection.updateLag();
+ if (clock == null) {
+ // updateLag() will have logged a message if we get here
+ return;
+ }
int drift = Math.abs(clock.getMeanLag());
String msg = String.format("Remote clock delayed between %d and %d us",
clock.minLag, clock.maxLag);
@@ -321,11 +330,13 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener {
}
void onReceiveRaw(String s) {
- if (TriggerMessage.isTriggerString(s)) {
- TriggerMessage tmsg = new TriggerMessage(s.substring(1).trim());
- onReceive(tmsg);
- } else {
- Log.i(TAG, "Malformed trigger data: " + s);
+ for (String trigger : s.split("\n")) {
+ if (TriggerMessage.isTriggerString(trigger)) {
+ TriggerMessage tmsg = new TriggerMessage(trigger.substring(1).trim());
+ onReceive(tmsg);
+ } else {
+ Log.i(TAG, "Malformed trigger data: " + s);
+ }
}
}
@@ -391,6 +402,12 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener {
}
public void stopListener() {
+ // If the trigger listener is already stopped, then it is possible the listener thread is
+ // null. In that case, calling stop() followed by join() will result in a listener object
+ // that is stuck in the STOPPING state.
+ if (triggerListener.isStopped()) {
+ return;
+ }
logger.log("Stopping Listener");
triggerListener.stop();
try {
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltTcpConnection.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltTcpConnection.java
index ee9c143..e63f4dc 100644
--- a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltTcpConnection.java
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltTcpConnection.java
@@ -32,8 +32,8 @@ import java.net.SocketTimeoutException;
public class WaltTcpConnection implements WaltConnection {
- // The local ip on ARC++ to connect to underlying ChromeOS
- private static final String SERVER_IP = "192.168.254.1";
+ // Use a "reverse" port over adb. The server is running on the host to which we're attached.
+ private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 50007;
private static final int TCP_READ_TIMEOUT_MS = 200;
@@ -74,6 +74,10 @@ public class WaltTcpConnection implements WaltConnection {
}
public void connect() {
+ // If the singleton is already connected, do not kill the connection.
+ if (isConnected()) {
+ return;
+ }
connectionState = Utils.ListenerState.STARTING;
networkThread = new HandlerThread("NetworkThread");
networkThread.start();
@@ -85,6 +89,7 @@ public class WaltTcpConnection implements WaltConnection {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVER_PORT);
+ socket.setKeepAlive(true);
socket.setSoTimeout(TCP_READ_TIMEOUT_MS);
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
@@ -119,18 +124,39 @@ public class WaltTcpConnection implements WaltConnection {
return connectionState == Utils.ListenerState.RUNNING;
}
- public void sendByte(char c) throws IOException {
- outputStream.write(Utils.char2byte(c));
+ public void sendByte(final char c) throws IOException {
+ // All network accesses must occur on a separate thread.
+ networkHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ outputStream.write(Utils.char2byte(c));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
}
- public void sendString(String s) throws IOException {
- outputStream.write(s.getBytes("UTF-8"));
+ public void sendString(final String s) throws IOException {
+ // All network accesses must occur on a separate thread.
+ networkHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ outputStream.write(s.getBytes("UTF-8"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
}
public synchronized int blockingRead(byte[] buff) {
messageReceived = false;
+ // All network accesses must occur on a separate thread.
networkHandler.post(new Runnable() {
@Override
public void run() {
@@ -171,8 +197,7 @@ public class WaltTcpConnection implements WaltConnection {
return lastRetVal;
}
-
- private void updateClock(String cmd) throws IOException {
+ private synchronized void updateClock(String cmd) throws IOException {
sendString(cmd);
int retval = blockingRead(buffer);
if (retval <= 0) {
@@ -181,8 +206,9 @@ public class WaltTcpConnection implements WaltConnection {
String s = new String(buffer, 0, retval);
String[] parts = s.trim().split("\\s+");
// TODO: make sure reply starts with "clock"
- long wallBaseTime = Long.parseLong(parts[1]);
- remoteClock.baseTime = wallBaseTime - RemoteClockInfo.uptimeZero();
+ // The bridge sends the time difference between when it sent the reply and when it zeroed
+ // the WALT's clock. We assume here that the reply transit time is negligible.
+ remoteClock.baseTime = RemoteClockInfo.microTime() - Long.parseLong(parts[1]);
remoteClock.minLag = Integer.parseInt(parts[2]);
remoteClock.maxLag = Integer.parseInt(parts[3]);
}