Skip to content
Snippets Groups Projects
Commit b7f7084c authored by akwizgran's avatar akwizgran
Browse files

Test throughput with Bluetooth disabled and enabled.

parent 55a1ea45
No related branches found
No related tags found
No related merge requests found
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();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment