Commit 30741380 authored by akwizgran's avatar akwizgran

Run a single test with download and upload.

parent 92111cc9
......@@ -7,19 +7,24 @@ import android.widget.EditText;
import android.widget.TextView;
import org.briarproject.interferometer.MainViewModel.ConnectionState;
import org.briarproject.interferometer.MainViewModel.TestPhase;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.DISCONNECTED;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.DOWNLOADING;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.UPLOADING;
import static org.briarproject.interferometer.MainViewModel.TestPhase.READY;
public class MainActivity extends AppCompatActivity {
private MainViewModel viewModel;
private EditText serverAddressEditText;
private TextView throughputTextView;
private String throughputFormat;
private ConnectionState state = DISCONNECTED;
private long throughput = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -28,40 +33,42 @@ public class MainActivity extends AppCompatActivity {
viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
serverAddressEditText = findViewById(R.id.server_address_edit_text);
throughputTextView = findViewById(R.id.throughput_text_view);
throughputFormat = getString(R.string.throughput_format);
showThroughput(0);
Button uploadButton = findViewById(R.id.upload_button);
viewModel.getConnectionState().observe(this, state -> {
if (state == UPLOADING) uploadButton.setText(R.string.stop_upload);
else uploadButton.setText(R.string.upload);
uploadButton.setEnabled(state != DOWNLOADING);
showThroughput(state, throughput);
Button testButton = findViewById(R.id.test_button);
viewModel.getTestPhase().observe(this, phase -> {
if (phase == READY) testButton.setText(R.string.start_test);
else testButton.setText(R.string.stop_test);
});
Button downloadButton = findViewById(R.id.download_button);
viewModel.getConnectionState().observe(this, state -> {
if (state == DOWNLOADING) downloadButton.setText(R.string.stop_download);
else downloadButton.setText(R.string.download);
downloadButton.setEnabled(state != UPLOADING);
this.state = state;
showThroughput(state, throughput);
});
viewModel.getThroughput().observe(this, this::showThroughput);
}
private void showThroughput(long throughput) {
String text = String.format(throughputFormat, throughput);
throughputTextView.setText(text);
viewModel.getThroughput().observe(this, throughput -> {
this.throughput = throughput;
showThroughput(state, throughput);
});
}
public void onUploadClick(View view) {
ConnectionState state = viewModel.getConnectionState().getValue();
if (state == UPLOADING) viewModel.disconnect();
else viewModel.startUpload(serverAddressEditText.getText().toString());
private void showThroughput(ConnectionState state, long throughput) {
if (state == DISCONNECTED) {
throughputTextView.setText(null);
} else if (state == DOWNLOADING) {
throughputTextView.setText(getString(R.string.download_format, throughput));
} else if (state == UPLOADING) {
throughputTextView.setText(getString(R.string.upload_format, throughput));
}
}
public void onDownloadClick(View view) {
ConnectionState state = viewModel.getConnectionState().getValue();
if (state == DOWNLOADING) viewModel.disconnect();
else viewModel.startDownload(serverAddressEditText.getText().toString());
public void onTestClick(View view) {
TestPhase phase = viewModel.getTestPhase().getValue();
if (phase == null || phase == READY) {
viewModel.startTest(serverAddressEditText.getText().toString());
} else {
viewModel.stopTest();
}
}
}
......@@ -11,6 +11,7 @@ import java.net.Socket;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
......@@ -22,9 +23,14 @@ import androidx.lifecycle.ViewModel;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.DISCONNECTED;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.DOWNLOADING;
import static org.briarproject.interferometer.MainViewModel.ConnectionState.UPLOADING;
import static org.briarproject.interferometer.MainViewModel.TestPhase.DOWNLOAD;
import static org.briarproject.interferometer.MainViewModel.TestPhase.READY;
import static org.briarproject.interferometer.MainViewModel.TestPhase.UPLOAD;
public class MainViewModel extends ViewModel {
enum TestPhase {READY, DOWNLOAD, UPLOAD}
enum ConnectionState {DISCONNECTED, DOWNLOADING, UPLOADING}
private static final byte CONNECTION_TYPE_UPLOAD = 0;
......@@ -36,11 +42,16 @@ public class MainViewModel extends ViewModel {
private static final String TAG = "Interferometer";
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final MutableLiveData<TestPhase> testPhase = new MutableLiveData<>();
private final MutableLiveData<ConnectionState> connectionState = new MutableLiveData<>();
private final MutableLiveData<Long> throughput = new MutableLiveData<>();
@Nullable
private Socket socket = null;
private Future<?> task = null;
LiveData<TestPhase> getTestPhase() {
return testPhase;
}
LiveData<ConnectionState> getConnectionState() {
return connectionState;
......@@ -51,42 +62,49 @@ public class MainViewModel extends ViewModel {
}
@UiThread
void startUpload(String serverAddress) {
closeSocket();
void startTest(String serverAddress) {
HostPort hostPort = parseServerAddress(serverAddress);
if (hostPort == null) return;
socket = new Socket();
executorService.execute(() -> upload(socket, hostPort));
}
task = executorService.submit(() -> {
try {
testPhase.postValue(DOWNLOAD);
long downThroughput = download(hostPort);
if (downThroughput == -1) {
Log.w(TAG, "Download failed");
return;
}
Log.i(TAG, "Download throughput: " + downThroughput);
@UiThread
void startDownload(String serverAddress) {
closeSocket();
HostPort hostPort = parseServerAddress(serverAddress);
if (hostPort == null) return;
socket = new Socket();
executorService.execute(() -> download(socket, hostPort));
testPhase.postValue(UPLOAD);
long upThroughput = upload(hostPort);
if (upThroughput == -1) {
Log.w(TAG, "Upload failed");
return;
}
Log.i(TAG, "Upload throughput: " + upThroughput);
} finally {
testPhase.postValue(READY);
}
});
}
@UiThread
void disconnect() {
closeSocket();
void stopTest() {
cancelTask();
}
@UiThread
@Override
protected void onCleared() {
executorService.shutdown();
closeSocket();
cancelTask();
}
@UiThread
private void closeSocket() {
if (socket != null) {
connectionState.setValue(DISCONNECTED);
throughput.setValue(0L);
tryToClose(socket);
socket = null;
private void cancelTask() {
if (task != null) {
task.cancel(true);
task = null;
}
}
......@@ -111,8 +129,9 @@ public class MainViewModel extends ViewModel {
}
}
private void upload(Socket s, HostPort hostPort) {
private long upload(HostPort hostPort) {
connectionState.postValue(UPLOADING);
Socket s = new Socket();
try {
Log.i(TAG, "Resolving " + hostPort.host);
InetAddress addr = InetAddress.getByName(hostPort.host);
......@@ -123,15 +142,20 @@ public class MainViewModel extends ViewModel {
OutputStream out = s.getOutputStream();
out.write(CONNECTION_TYPE_UPLOAD);
Random random = new Random();
byte[] buf = new byte[BUFFER_SIZE];
new Random().nextBytes(buf);
long start = System.nanoTime();
long lastUpdate = 0;
int bytesSinceLastUpdate = 0;
int bytesTotal = 0;
while (bytesTotal < BYTES_TO_TRANSFER) {
int len = Math.min(BUFFER_SIZE, BYTES_TO_TRANSFER - bytesTotal);
random.nextBytes(buf);
out.write(buf, 0, len);
if (Thread.currentThread().isInterrupted()) {
Log.i(TAG, "Upload to " + hostPort + " interrupted");
return -1;
}
bytesSinceLastUpdate += len;
bytesTotal += len;
long now = System.nanoTime();
......@@ -144,8 +168,12 @@ public class MainViewModel extends ViewModel {
}
}
Log.i(TAG, "Upload to " + hostPort + " finished");
long now = System.nanoTime();
long msSinceStart = (now - start) / 1_000_000;
return bytesTotal * 1000L / msSinceStart;
} catch (IOException e) {
Log.w(TAG, "Exception from " + hostPort + ": " + e);
return -1;
} finally {
connectionState.postValue(DISCONNECTED);
throughput.postValue(0L);
......@@ -153,8 +181,9 @@ public class MainViewModel extends ViewModel {
}
}
private void download(Socket s, HostPort hostPort) {
private long download(HostPort hostPort) {
connectionState.postValue(DOWNLOADING);
Socket s = new Socket();
try {
Log.i(TAG, "Resolving " + hostPort.host);
InetAddress addr = InetAddress.getByName(hostPort.host);
......@@ -167,15 +196,21 @@ public class MainViewModel extends ViewModel {
InputStream in = s.getInputStream();
byte[] buf = new byte[BUFFER_SIZE];
long start = System.nanoTime();
long lastUpdate = 0;
int bytesSinceLastUpdate = 0;
int bytesTotal = 0;
while (bytesTotal < BYTES_TO_TRANSFER) {
int len = Math.min(BUFFER_SIZE, BYTES_TO_TRANSFER - bytesTotal);
int read = in.read(buf, 0, len);
if (read == -1) {
Log.w(TAG, "Download from " + hostPort + " finished early");
return;
return -1;
}
if (Thread.currentThread().isInterrupted()) {
Log.i(TAG, "Download from " + hostPort + " interrupted");
return -1;
}
bytesSinceLastUpdate += read;
bytesTotal += read;
......@@ -189,8 +224,12 @@ public class MainViewModel extends ViewModel {
}
}
Log.i(TAG, "Download from " + hostPort + " finished");
long now = System.nanoTime();
long msSinceStart = (now - start) / 1_000_000;
return bytesTotal * 1000L / msSinceStart;
} catch (IOException e) {
Log.w(TAG, "Exception from " + hostPort + ": " + e);
return -1;
} finally {
connectionState.postValue(DISCONNECTED);
throughput.postValue(0L);
......
......@@ -17,31 +17,14 @@
android:inputType="textUri"
tools:ignore="Autofill" />
<LinearLayout
android:layout_width="match_parent"
<Button
android:id="@+id/test_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:onClick="onDownloadClick"
android:text="@string/download"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/upload_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:onClick="onUploadClick"
android:text="@string/upload"
tools:ignore="ButtonStyle" />
</LinearLayout>
android:layout_margin="16dp"
android:onClick="onTestClick"
android:text="@string/start_test"
tools:ignore="ButtonStyle" />
<TextView
android:id="@+id/throughput_text_view"
......
......@@ -2,10 +2,10 @@
<string name="app_name">Interferometer</string>
<string name="server_address">Server address</string>
<string name="download">Download</string>
<string name="upload">Upload</string>
<string name="stop_download">Stop download</string>
<string name="stop_upload">Stop upload</string>
<string name="throughput_format">Throughput: %,d bytes/sec</string>
<string name="start_test">Start test</string>
<string name="stop_test">Stop test</string>
<string name="download_format">Download: %,d bytes/sec</string>
<string name="upload_format">Upload: %,d bytes/sec</string>
</resources>
......@@ -50,12 +50,9 @@ public class Server {
} else if (connectionType == CONNECTION_TYPE_DOWNLOAD) {
LOG.info("Download connection from " + remote);
OutputStream out = s.getOutputStream();
Random random = new Random();
new Random().nextBytes(buf);
//noinspection InfiniteLoopStatement
while (true) {
random.nextBytes(buf);
out.write(buf);
}
while (true) out.write(buf);
}
s.close();
} catch (IOException e) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment