Commit b7f7084c authored by akwizgran's avatar akwizgran

Test throughput with Bluetooth disabled and enabled.

parent 55a1ea45
package org.briarproject.interferometer;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
......@@ -17,23 +24,29 @@ import java.util.concurrent.Future;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
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.StartResult.INVALID_ADDRESS;
import static org.briarproject.interferometer.MainViewModel.StartResult.NO_BLUETOOTH;
import static org.briarproject.interferometer.MainViewModel.StartResult.STARTED;
import static org.briarproject.interferometer.MainViewModel.TestPhase.DOWNLOAD;
import static org.briarproject.interferometer.MainViewModel.TestPhase.BASELINE;
import static org.briarproject.interferometer.MainViewModel.TestPhase.BLUETOOTH;
import static org.briarproject.interferometer.MainViewModel.TestPhase.READY;
import static org.briarproject.interferometer.MainViewModel.TestPhase.UPLOAD;
import static org.briarproject.interferometer.Statistics.mean;
public class MainViewModel extends ViewModel {
public class MainViewModel extends AndroidViewModel {
enum TestPhase {READY, DOWNLOAD, UPLOAD}
enum TestPhase {READY, BASELINE, BLUETOOTH}
enum ConnectionState {DISCONNECTED, DOWNLOADING, UPLOADING}
......@@ -41,9 +54,11 @@ public class MainViewModel extends ViewModel {
private static final byte CONNECTION_TYPE_UPLOAD = 0;
private static final byte CONNECTION_TYPE_DOWNLOAD = 1;
private static final int BUFFER_SIZE = 4096;
private static final int MS_PER_UPDATE = 250;
private static final int BYTES_TO_TRANSFER = 100_000_000;
private static final int BYTES_TO_TRANSFER = 10_000_000;
private static final int TRANSFERS_PER_CONDITION = 10;
private static final String TAG = "Interferometer";
......@@ -55,6 +70,10 @@ public class MainViewModel extends ViewModel {
@Nullable
private Future<?> task = null;
public MainViewModel(@NonNull Application application) {
super(application);
}
LiveData<TestPhase> getTestPhase() {
return testPhase;
}
......@@ -75,21 +94,31 @@ public class MainViewModel extends ViewModel {
if (adapter == null) return NO_BLUETOOTH;
task = executorService.submit(() -> {
try {
testPhase.postValue(DOWNLOAD);
long downThroughput = download(hostPort);
if (downThroughput == -1) {
Log.w(TAG, "Download failed");
if (!disableBluetooth(adapter)) {
Log.w(TAG, "Failed to disable Bluetooth");
return;
}
Log.i(TAG, "Download throughput: " + downThroughput);
testPhase.postValue(UPLOAD);
long upThroughput = upload(hostPort);
if (upThroughput == -1) {
Log.w(TAG, "Upload failed");
testPhase.postValue(BASELINE);
Log.i(TAG, "Testing baseline throughput");
List<Long> downloadBaseline = new ArrayList<>();
List<Long> uploadBaseline = new ArrayList<>();
if (!testThroughput(hostPort, downloadBaseline, uploadBaseline)) return;
if (!enableBluetooth(adapter)) {
Log.w(TAG, "Failed to enable Bluetooth");
return;
}
Log.i(TAG, "Upload throughput: " + upThroughput);
testPhase.postValue(BLUETOOTH);
Log.i(TAG, "Testing throughput with Bluetooth enabled");
List<Long> downloadBt = new ArrayList<>();
List<Long> uploadBt = new ArrayList<>();
if (!testThroughput(hostPort, downloadBt, uploadBt)) return;
Log.i(TAG, "Test finished");
} catch (InterruptedException e) {
Log.w(TAG, "Interrupted");
} finally {
testPhase.postValue(READY);
}
......@@ -117,14 +146,6 @@ public class MainViewModel extends ViewModel {
}
}
private void tryToClose(Socket s) {
try {
s.close();
} catch (IOException e) {
// Ignored
}
}
@Nullable
private HostPort parseServerAddress(String s) {
int index = s.indexOf(":");
......@@ -138,14 +159,66 @@ public class MainViewModel extends ViewModel {
}
}
private boolean disableBluetooth(BluetoothAdapter adapter) throws InterruptedException {
if (!adapter.isEnabled()) return true;
BluetoothStateReceiver receiver = new BluetoothStateReceiver(STATE_OFF);
Application app = getApplication();
app.registerReceiver(receiver, new IntentFilter(ACTION_STATE_CHANGED));
try {
if (!adapter.disable()) return false;
if (!adapter.isEnabled()) return true;
return receiver.waitForTargetState();
} finally {
app.unregisterReceiver(receiver);
}
}
private boolean enableBluetooth(BluetoothAdapter adapter) throws InterruptedException {
if (adapter.isEnabled()) return true;
BluetoothStateReceiver receiver = new BluetoothStateReceiver(STATE_ON);
Application app = getApplication();
app.registerReceiver(receiver, new IntentFilter(ACTION_STATE_CHANGED));
try {
if (!adapter.enable()) return false;
if (adapter.isEnabled()) return true;
return receiver.waitForTargetState();
} finally {
app.unregisterReceiver(receiver);
}
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean testThroughput(HostPort hostPort, List<Long> downloadThroughput,
List<Long> uploadThroughput) {
for (int i = 0; i < TRANSFERS_PER_CONDITION; i++) {
long throughput = download(hostPort);
if (throughput == -1) {
Log.w(TAG, "Download failed");
return false;
}
Log.i(TAG, "Download throughput: " + throughput);
downloadThroughput.add(throughput);
throughput = upload(hostPort);
if (throughput == -1) {
Log.w(TAG, "Upload failed");
return false;
}
Log.i(TAG, "Upload throughput: " + throughput);
uploadThroughput.add(throughput);
}
Log.i(TAG, "Mean download throughput: " + mean(downloadThroughput));
Log.i(TAG, "Mean upload throughput: " + mean(uploadThroughput));
return true;
}
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);
Log.i(TAG, "Connecting to " + hostPort);
s.connect(new InetSocketAddress(addr, hostPort.port));
s.connect(new InetSocketAddress(hostPort.host, hostPort.port));
Log.i(TAG, "Connected, requesting upload");
OutputStream out = s.getOutputStream();
......@@ -194,10 +267,8 @@ public class MainViewModel extends ViewModel {
connectionState.postValue(DOWNLOADING);
Socket s = new Socket();
try {
Log.i(TAG, "Resolving " + hostPort.host);
InetAddress addr = InetAddress.getByName(hostPort.host);
Log.i(TAG, "Connecting to " + hostPort + " for download");
s.connect(new InetSocketAddress(addr, hostPort.port));
Log.i(TAG, "Connecting to " + hostPort);
s.connect(new InetSocketAddress(hostPort.host, hostPort.port));
Log.i(TAG, "Connected, requesting download");
OutputStream out = s.getOutputStream();
......@@ -246,6 +317,14 @@ public class MainViewModel extends ViewModel {
}
}
private void tryToClose(Socket s) {
try {
s.close();
} catch (IOException e) {
// Ignored
}
}
private static class HostPort {
private final String host;
......@@ -262,4 +341,30 @@ public class MainViewModel extends ViewModel {
return host + ":" + port;
}
}
private static class BluetoothStateReceiver extends BroadcastReceiver {
private static final int TIMEOUT = 10_000;
private final CountDownLatch latch = new CountDownLatch(1);
private final int targetState;
private BluetoothStateReceiver(int targetState) {
this.targetState = targetState;
}
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
Log.i(TAG, "Bluetooth state changed to " + state);
if (state == targetState) {
Log.i(TAG, "Reached target state");
latch.countDown();
}
}
private boolean waitForTargetState() throws InterruptedException {
return latch.await(TIMEOUT, MILLISECONDS);
}
}
}
package org.briarproject.interferometer;
import java.util.List;
class Statistics {
static <T extends Number> double mean(List<T> values) {
if (values.isEmpty()) throw new IllegalArgumentException();
double total = 0;
for (T value : values) total += value.doubleValue();
return total / values.size();
}
}
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