Compare commits

...

4 Commits

Author SHA1 Message Date
goapunk
a51f0f803f add bt polling speedtest 2018-05-10 14:42:09 +02:00
akwizgran
8f9d7a70bf Pause between connection attempts. 2018-05-08 14:15:39 +01:00
akwizgran
3ea642c6c0 Don't poll again if last poll is still running. 2018-05-08 13:51:39 +01:00
akwizgran
da0a32c613 Poll contacts in series rather than parallel. 2018-05-08 13:51:31 +01:00
8 changed files with 300 additions and 31 deletions

View File

@@ -24,7 +24,9 @@ import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -53,6 +55,7 @@ class Poller implements EventListener {
private final Clock clock; private final Clock clock;
private final Lock lock; private final Lock lock;
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
private final Set<TransportId> polling; // Locking: lock
@Inject @Inject
Poller(@IoExecutor Executor ioExecutor, Poller(@IoExecutor Executor ioExecutor,
@@ -69,16 +72,19 @@ class Poller implements EventListener {
this.clock = clock; this.clock = clock;
lock = new ReentrantLock(); lock = new ReentrantLock();
tasks = new HashMap<>(); tasks = new HashMap<>();
polling = new HashSet<>();
} }
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) { if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e; ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
/*
if (c.isActive()) { if (c.isActive()) {
// Connect to the newly activated contact // Connect to the newly activated contact
connectToContact(c.getContactId()); connectToContact(c.getContactId());
} }
*/
} else if (e instanceof ConnectionClosedEvent) { } else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e; ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased // Reschedule polling, the polling interval may have decreased
@@ -215,20 +221,33 @@ class Poller implements EventListener {
@Override @Override
@IoExecutor @IoExecutor
public void run() { public void run() {
TransportId t = plugin.getId();
boolean shouldPoll;
lock.lock(); lock.lock();
try { try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t); ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this) if (scheduled != null && scheduled.task != this)
return; // Replaced by another task return; // Replaced by another task
tasks.remove(t); tasks.remove(t);
// Don't poll again if last poll is still running
shouldPoll = polling.add(t);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
int delay = plugin.getPollingInterval(); int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble()); if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false); schedule(plugin, delay, false);
if (shouldPoll) {
poll(plugin); poll(plugin);
} else if (LOG.isLoggable(INFO)) {
LOG.info("Last poll for " + t + " is still running");
}
lock.lock();
try {
polling.remove(t);
} finally {
lock.unlock();
}
} }
} }
} }

View File

@@ -26,9 +26,11 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -53,6 +55,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName()); Logger.getLogger(BluetoothPlugin.class.getName());
/**
* How many milliseconds to pause between connection attempts when
* polling, to avoid interfering with other Bluetooth or wifi connections.
*/
private static final int POLLING_PAUSE_MS = 3000;
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -253,27 +261,42 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment(); backoff.increment();
// Try to connect to known devices in parallel // Try to connect to known devices in a random order
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties(); callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { List<ContactId> keys = new ArrayList<>(remote.keySet());
ContactId c = e.getKey(); Collections.shuffle(keys);
if (connected.contains(c)) continue;
String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
boolean first = true;
for (ContactId c : keys) {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return; if (!connectionLimiter.canOpenContactConnection()) return;
if (connected.contains(c)) continue;
TransportProperties p = remote.get(c);
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
// Pause between connection attempts
if (first) {
first = false;
} else {
try {
Thread.sleep(POLLING_PAUSE_MS);
} catch (InterruptedException ex) {
LOG.info("Interrupted while polling");
Thread.currentThread().interrupt();
return;
}
}
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) { if (conn != null) {
backoff.reset(); backoff.reset();
if (connectionLimiter.contactConnectionOpened(conn)) if (connectionLimiter.contactConnectionOpened(conn))
callback.outgoingConnectionCreated(c, conn); callback.outgoingConnectionCreated(c, conn);
} }
});
} }
});
} }
@Nullable @Nullable

View File

@@ -33,6 +33,10 @@ dependencies {
implementation 'com.jpardogo.materialtabstrip:library:1.1.0' implementation 'com.jpardogo.materialtabstrip:library:1.1.0'
implementation 'com.github.bumptech.glide:glide:3.8.0' implementation 'com.github.bumptech.glide:glide:3.8.0'
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0' implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
implementation'fr.bmartel:jspeedtest:1.32.0'
implementation 'com.jjoe64:graphview:4.2.2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'

View File

@@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<application <application
@@ -69,8 +70,8 @@
<activity <activity
android:name="org.briarproject.briar.android.splash.SplashScreenActivity" android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar" android:label="@string/app_name"
android:label="@string/app_name"> android:theme="@style/BriarTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
@@ -84,15 +85,15 @@
<activity <activity
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity" android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:launchMode="singleTop"
android:launchMode="singleTop"> android:theme="@style/BriarTheme.NoActionBar">
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.contact.ConversationActivity" android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="stateHidden|adjustResize"> android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
@@ -304,8 +305,8 @@
<activity <activity
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity" android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title" android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"> android:theme="@style/BriarTheme.NoActionBar">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
@@ -362,6 +363,16 @@
/> />
</activity> </activity>
<activity
android:name="org.briarproject.briar.android.test.PollingTestActivity"
android:label="Test polling"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
/>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity" android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting" android:label="@string/panic_setting"

View File

@@ -70,6 +70,7 @@ import org.briarproject.briar.android.sharing.ShareForumFragment;
import org.briarproject.briar.android.sharing.ShareForumMessageFragment; import org.briarproject.briar.android.sharing.ShareForumMessageFragment;
import org.briarproject.briar.android.sharing.SharingModule; import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.splash.SplashScreenActivity;
import org.briarproject.briar.android.test.PollingTestActivity;
import org.briarproject.briar.android.test.TestDataActivity; import org.briarproject.briar.android.test.TestDataActivity;
import dagger.Component; import dagger.Component;
@@ -150,6 +151,8 @@ public interface ActivityComponent {
void inject(TestDataActivity activity); void inject(TestDataActivity activity);
void inject(PollingTestActivity activity);
void inject(ChangePasswordActivity activity); void inject(ChangePasswordActivity activity);
void inject(IntroductionActivity activity); void inject(IntroductionActivity activity);

View File

@@ -0,0 +1,176 @@
package org.briarproject.briar.android.test;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.MenuItem;
import android.widget.Button;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.api.test.TestDataCreator;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Logger;
import javax.inject.Inject;
import fr.bmartel.speedtest.SpeedTestReport;
import fr.bmartel.speedtest.SpeedTestSocket;
import fr.bmartel.speedtest.inter.ISpeedTestListener;
import fr.bmartel.speedtest.model.SpeedTestError;
public class PollingTestActivity extends BriarActivity {
private static final Logger LOG =
Logger.getLogger(PollingTestActivity.class.getName());
// Add one contact after each round up to:
private static int NUMBER_OF_CONTACTS = 10;
// Download x times per #contacts
private static int NUMBER_OF_DOWNLOADS = 1;
// File size to download. One of (5,10,50,100,500)MB.
private static int DOWNLOAD_SIZE = 10;
// Time to wait between each round in ms.
private static int PAUSE = 5000;
// Socket timeout in ms
private static int SO_TIMEOUT = 60000;
@Inject
TestDataCreator testDataCreator;
@Inject
AndroidExecutor executor;
@Inject
EventBus eventBus;
GraphView graphView;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
setContentView(R.layout.activity_test_polling);
Button button = findViewById(R.id.run_test_button);
button.setOnClickListener(v -> {
runTest();
});
graphView = findViewById(R.id.graph);
graphView.getGridLabelRenderer()
.setHorizontalAxisTitle("Number of contacts");
graphView.getGridLabelRenderer().setVerticalAxisTitle("MBit/s");
graphView.getViewport().setXAxisBoundsManual(true);
graphView.getViewport().setMinX(0);
graphView.getViewport().setMaxX(NUMBER_OF_CONTACTS);
}
private void runTest() {
executor.runOnBackgroundThread(() -> {
LineGraphSeries<DataPoint> series = new LineGraphSeries<>();
graphView.addSeries(series);
for (int i = 0; i < NUMBER_OF_CONTACTS; i++) {
if (i != 0) {
createContact();
// Wait to let the previous round of polling settle.
synchronized (this) {
try {
wait(PAUSE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// Reset BT polling.
eventBus.broadcast(new TransportDisabledEvent(
BluetoothConstants.ID));
eventBus.broadcast(
new TransportEnabledEvent(BluetoothConstants.ID));
LOG.info("\n###########\nRound " + i + " (Contacts)");
double y = run();
series.appendData(new DataPoint(i, y), false,
NUMBER_OF_CONTACTS);
}
});
}
private double run() {
SpeedTestReport reports[] = new SpeedTestReport[3];
double average = 0;
for (int i = 0; i < NUMBER_OF_DOWNLOADS; i++) {
final CountDownLatch cl = new CountDownLatch(1);
SpeedTestSocket speedTestSocket = new SpeedTestSocket();
speedTestSocket.setSocketTimeout(SO_TIMEOUT);
int finalI = i;
speedTestSocket.addSpeedTestListener(new ISpeedTestListener() {
@Override
public void onCompletion(SpeedTestReport report) {
LOG.info("[COMPLETED]: rate in bit/s : " +
report.getTransferRateBit() + " in " +
(report.getReportTime() -
report.getStartTime()) + "ms");
reports[finalI] = report;
cl.countDown();
}
@Override
public void onProgress(float percent,
SpeedTestReport report) {
/* LOG.info("[PROGRESS] rate in bit/s : " +
report.getTransferRateBit());
*/
}
@Override
public void onError(SpeedTestError speedTestError,
String errorMessage) {
LOG.info("Error: " + speedTestError.name() + " : " +
errorMessage);
}
});
LOG.info("Download " + (i + 1) + " of " + NUMBER_OF_DOWNLOADS);
speedTestSocket
.startDownload(
"http://ikoula.testdebit.info/" + DOWNLOAD_SIZE +
"M.iso");
try {
cl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
average += reports[i].getTransferRateBit().doubleValue() / 1000000;
}
return average / NUMBER_OF_DOWNLOADS;
}
private void createContact() {
testDataCreator.createTestData(1, 0, 0, 0, 0);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Button
android:id="@+id/run_test_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Run test"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<com.jjoe64.graphview.GraphView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/run_test_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/graph" />
</android.support.constraint.ConstraintLayout>

View File

@@ -131,4 +131,13 @@
android:targetPackage="@string/app_package"/> android:targetPackage="@string/app_package"/>
</Preference> </Preference>
<Preference
android:key="pref_key_speed_test"
android:title="Test polling impact on bandwith">
<intent
android:targetClass="org.briarproject.briar.android.test.PollingTestActivity"
android:targetPackage="@string/app_package"/>
</Preference>
</PreferenceScreen> </PreferenceScreen>