From 69f23ead9bd68eeb84767e1a2684c80323814149 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 5 May 2016 17:51:55 +0100 Subject: [PATCH 1/5] Ensure that Service instances aren't reused. --- .../AndroidNotificationManagerImpl.java | 34 ++++++++++++++----- .../plugins/PluginManagerImpl.java | 3 ++ .../sync/ValidationManagerImpl.java | 8 +++++ .../transport/KeyManagerImpl.java | 3 ++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index 0a7b1ea2b..ee487ebc1 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -41,6 +41,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.inject.Inject; @@ -80,10 +81,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private final Context appContext; // The following must only be accessed on the main UI thread - private final Map contactCounts = - new HashMap(); - private final Map forumCounts = - new HashMap(); + private final Map contactCounts = new HashMap<>(); + private final Map forumCounts = new HashMap<>(); + private final AtomicBoolean used = new AtomicBoolean(false); + private int contactTotal = 0, forumTotal = 0; private int nextRequestId = 0; private GroupId visibleGroup = null; @@ -105,6 +106,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, @Override public void startService() throws ServiceException { + if (used.getAndSet(true)) throw new IllegalStateException(); try { settings = settingsManager.getSettings(SETTINGS_NAMESPACE); } catch (DbException e) { @@ -115,6 +117,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, @Override public void stopService() throws ServiceException { Future f = androidExecutor.submit(new Callable() { + @Override public Void call() { clearPrivateMessageNotification(); clearForumPostNotification(); @@ -124,9 +127,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, }); try { f.get(); - } catch (InterruptedException e) { - throw new ServiceException(e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { throw new ServiceException(e); } } @@ -149,6 +150,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, nm.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID); } + @Override public void eventOccurred(Event e) { if (e instanceof SettingsUpdatedEvent) { SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; @@ -176,6 +178,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private void loadSettings() { dbExecutor.execute(new Runnable() { + @Override public void run() { try { settings = settingsManager.getSettings(SETTINGS_NAMESPACE); @@ -187,8 +190,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, }); } + @Override public void showPrivateMessageNotification(final GroupId g) { androidExecutor.execute(new Runnable() { + @Override public void run() { Integer count = contactCounts.get(g); if (count == null) contactCounts.put(g, 1); @@ -200,8 +205,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, }); } + @Override public void clearPrivateMessageNotification(final GroupId g) { androidExecutor.execute(new Runnable() { + @Override public void run() { Integer count = contactCounts.remove(g); if (count == null) return; // Already cleared @@ -271,9 +278,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, return defaults; } + @Override public void showForumPostNotification(final GroupId g) { androidExecutor.execute(new Runnable() { - public void run() { + @Override + public void + run() { Integer count = forumCounts.get(g); if (count == null) forumCounts.put(g, 1); else forumCounts.put(g, count + 1); @@ -284,8 +294,10 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, }); } + @Override public void clearForumPostNotification(final GroupId g) { androidExecutor.execute(new Runnable() { + @Override public void run() { Integer count = forumCounts.remove(g); if (count == null) return; // Already cleared @@ -343,16 +355,20 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, } } + @Override public void blockNotification(final GroupId g) { androidExecutor.execute(new Runnable() { + @Override public void run() { visibleGroup = g; } }); } + @Override public void unblockNotification(final GroupId g) { androidExecutor.execute(new Runnable() { + @Override public void run() { if (g.equals(visibleGroup)) visibleGroup = null; } @@ -361,6 +377,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private void showIntroductionNotifications(final ContactId c) { androidExecutor.execute(new Runnable() { + @Override public void run() { try { GroupId group = messagingManager.getConversationId(c); @@ -375,6 +392,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private void showIntroductionSucceededNotification(final Contact c) { androidExecutor.execute(new Runnable() { + @Override public void run() { NotificationCompat.Builder b = new NotificationCompat.Builder(appContext); diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index b93756b18..852fe3dc9 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -39,6 +39,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.inject.Inject; @@ -61,6 +62,7 @@ class PluginManagerImpl implements PluginManager, Service { private final Map plugins; private final List simplexPlugins; private final List duplexPlugins; + private final AtomicBoolean used = new AtomicBoolean(false); @Inject PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, @@ -82,6 +84,7 @@ class PluginManagerImpl implements PluginManager, Service { @Override public void startService() throws ServiceException { + if (used.getAndSet(true)) throw new IllegalStateException(); Collection simplexFactories = pluginConfig.getSimplexFactories(); Collection duplexFactories = diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java index a5d02b5cc..52121c706 100644 --- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java +++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.inject.Inject; @@ -44,6 +45,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private final Executor cryptoExecutor; private final Map validators; private final Map hooks; + private final AtomicBoolean used = new AtomicBoolean(false); @Inject ValidationManagerImpl(DatabaseComponent db, @@ -58,6 +60,7 @@ class ValidationManagerImpl implements ValidationManager, Service, @Override public void startService() { + if (used.getAndSet(true)) throw new IllegalStateException(); for (ClientId c : validators.keySet()) getMessagesToValidate(c); } @@ -78,6 +81,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private void getMessagesToValidate(final ClientId c) { dbExecutor.execute(new Runnable() { + @Override public void run() { try { Queue unvalidated = new LinkedList(); @@ -100,6 +104,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private void validateNextMessage(final Queue unvalidated) { if (unvalidated.isEmpty()) return; dbExecutor.execute(new Runnable() { + @Override public void run() { try { Message m = null; @@ -141,6 +146,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private void validateMessage(final Message m, final Group g) { cryptoExecutor.execute(new Runnable() { + @Override public void run() { MessageValidator v = validators.get(g.getClientId()); if (v == null) { @@ -156,6 +162,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private void storeValidationResult(final Message m, final ClientId c, final Metadata meta) { dbExecutor.execute(new Runnable() { + @Override public void run() { try { Transaction txn = db.startTransaction(false); @@ -193,6 +200,7 @@ class ValidationManagerImpl implements ValidationManager, Service, private void loadGroupAndValidate(final Message m) { dbExecutor.execute(new Runnable() { + @Override public void run() { try { Group g; diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java index fdcbe8f30..03b2c1366 100644 --- a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java +++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java @@ -28,6 +28,7 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.inject.Inject; @@ -47,6 +48,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { private final Clock clock; private final Map activeContacts; private final ConcurrentHashMap managers; + private final AtomicBoolean used = new AtomicBoolean(false); @Inject KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, @@ -66,6 +68,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { @Override public void startService() throws ServiceException { + if (used.getAndSet(true)) throw new IllegalStateException(); Map transports = new HashMap(); for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) From 2ecccc66d10de7003d6bbeddef6faa9120fd156b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 5 May 2016 18:05:53 +0100 Subject: [PATCH 2/5] Ensure that Plugin instances aren't reused. --- .../AndroidNotificationManagerImpl.java | 3 +- .../plugins/droidtooth/DroidtoothPlugin.java | 44 +++++++++++++------ .../plugins/tcp/AndroidLanTcpPlugin.java | 1 + .../briarproject/plugins/tor/TorPlugin.java | 33 +++++++++++++- .../briarproject/plugins/file/FilePlugin.java | 8 ++++ .../plugins/tcp/LanTcpPlugin.java | 1 + .../briarproject/plugins/tcp/TcpPlugin.java | 22 +++++++++- .../plugins/tcp/WanTcpPlugin.java | 1 + .../plugins/bluetooth/BluetoothPlugin.java | 39 +++++++++++----- .../plugins/file/RemovableDrivePlugin.java | 14 ++++-- .../plugins/modem/ModemPlugin.java | 22 +++++++++- 11 files changed, 153 insertions(+), 35 deletions(-) diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index ee487ebc1..669722b1d 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -282,8 +282,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, public void showForumPostNotification(final GroupId g) { androidExecutor.execute(new Runnable() { @Override - public void - run() { + public void run() { Integer count = forumCounts.get(g); if (count == null) forumCounts.put(g, 1); else forumCounts.put(g, count + 1); diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java index 40a8a0410..a89216505 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java @@ -9,9 +9,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import org.briarproject.android.api.AndroidExecutor; import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.TransportId; -import org.briarproject.android.api.AndroidExecutor; import org.briarproject.api.contact.ContactId; import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.keyagreement.KeyAgreementConnection; @@ -41,6 +41,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; @@ -80,6 +81,7 @@ class DroidtoothPlugin implements DuplexPlugin { private final Backoff backoff; private final DuplexPluginCallback callback; private final int maxLatency; + private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; private volatile boolean wasEnabledByUs = false; @@ -101,24 +103,30 @@ class DroidtoothPlugin implements DuplexPlugin { this.maxLatency = maxLatency; } + @Override public TransportId getId() { return ID; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { // Bluetooth detects dead connections so we don't need keepalives return Integer.MAX_VALUE; } + @Override public boolean start() throws IOException { + if (used.getAndSet(true)) throw new IllegalStateException(); // BluetoothAdapter.getDefaultAdapter() must be called on a thread // with a message queue, so submit it to the AndroidExecutor try { adapter = androidExecutor.submit(new Callable() { + @Override public BluetoothAdapter call() throws Exception { return BluetoothAdapter.getDefaultAdapter(); } @@ -158,6 +166,7 @@ class DroidtoothPlugin implements DuplexPlugin { private void bind() { ioExecutor.execute(new Runnable() { + @Override public void run() { if (!isRunning()) return; String address = AndroidUtils.getBluetoothAddress(appContext, @@ -238,6 +247,7 @@ class DroidtoothPlugin implements DuplexPlugin { return new DroidtoothTransportConnection(this, s); } + @Override public void stop() { running = false; if (receiver != null) appContext.unregisterReceiver(receiver); @@ -249,18 +259,22 @@ class DroidtoothPlugin implements DuplexPlugin { } } + @Override public boolean isRunning() { return running && adapter.isEnabled(); } + @Override public boolean shouldPoll() { return true; } + @Override public int getPollingInterval() { return backoff.getPollingInterval(); } + @Override public void poll(Collection connected) { if (!isRunning()) return; backoff.increment(); @@ -275,6 +289,7 @@ class DroidtoothPlugin implements DuplexPlugin { final String uuid = e.getValue().get(PROP_UUID); if (StringUtils.isNullOrEmpty(uuid)) continue; ioExecutor.execute(new Runnable() { + @Override public void run() { if (!running) return; BluetoothSocket s = connect(address, uuid); @@ -327,6 +342,7 @@ class DroidtoothPlugin implements DuplexPlugin { } } + @Override public DuplexTransportConnection createConnection(ContactId c) { if (!isRunning()) return null; TransportProperties p = callback.getRemoteProperties().get(c); @@ -340,10 +356,12 @@ class DroidtoothPlugin implements DuplexPlugin { return new DroidtoothTransportConnection(this, s); } + @Override public boolean supportsInvitations() { return true; } + @Override public DuplexTransportConnection createInvitationConnection(PseudoRandom r, long timeout, boolean alice) { if (!isRunning()) return null; @@ -361,9 +379,8 @@ class DroidtoothPlugin implements DuplexPlugin { } // Create the background tasks CompletionService complete = - new ExecutorCompletionService(ioExecutor); - List> futures = - new ArrayList>(); + new ExecutorCompletionService<>(ioExecutor); + List> futures = new ArrayList<>(); if (alice) { // Return the first connected socket futures.add(complete.submit(new ListeningTask(ss))); @@ -398,6 +415,7 @@ class DroidtoothPlugin implements DuplexPlugin { private void closeSockets(final List> futures, final BluetoothSocket chosen) { ioExecutor.execute(new Runnable() { + @Override public void run() { for (Future f : futures) { try { @@ -413,9 +431,7 @@ class DroidtoothPlugin implements DuplexPlugin { } catch (InterruptedException e) { LOG.info("Interrupted while closing sockets"); return; - } catch (ExecutionException e) { - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); - } catch (IOException e) { + } catch (ExecutionException | IOException e) { if (LOG.isLoggable(INFO)) LOG.info(e.toString()); } } @@ -423,14 +439,15 @@ class DroidtoothPlugin implements DuplexPlugin { }); } + @Override public boolean supportsKeyAgreement() { return true; } - public KeyAgreementListener createKeyAgreementListener( - byte[] localCommitment) { + @Override + public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { // No truncation necessary because COMMIT_LENGTH = 16 - UUID uuid = UUID.nameUUIDFromBytes(localCommitment); + UUID uuid = UUID.nameUUIDFromBytes(commitment); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); // Bind a server socket for receiving invitation connections BluetoothServerSocket ss; @@ -448,8 +465,9 @@ class DroidtoothPlugin implements DuplexPlugin { return new BluetoothKeyAgreementListener(d, ss); } + @Override public DuplexTransportConnection createKeyAgreementConnection( - byte[] remoteCommitment, TransportDescriptor d, long timeout) { + byte[] commitment, TransportDescriptor d, long timeout) { if (!isRunning()) return null; if (!ID.equals(d.getIdentifier())) return null; TransportProperties p = d.getProperties(); @@ -457,7 +475,7 @@ class DroidtoothPlugin implements DuplexPlugin { String address = p.get(PROP_ADDRESS); if (StringUtils.isNullOrEmpty(address)) return null; // No truncation necessary because COMMIT_LENGTH = 16 - UUID uuid = UUID.nameUUIDFromBytes(remoteCommitment); + UUID uuid = UUID.nameUUIDFromBytes(commitment); if (LOG.isLoggable(INFO)) LOG.info("Connecting to key agreement UUID " + uuid); BluetoothSocket s = connect(address, uuid.toString()); @@ -533,7 +551,7 @@ class DroidtoothPlugin implements DuplexPlugin { private static class DiscoveryReceiver extends BroadcastReceiver { private final CountDownLatch finished = new CountDownLatch(1); - private final List addresses = new ArrayList(); + private final List addresses = new ArrayList<>(); @Override public void onReceive(Context ctx, Intent intent) { diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java index e04e2f4fc..598e914b6 100644 --- a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java @@ -35,6 +35,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { @Override public boolean start() { + if (used.getAndSet(true)) throw new IllegalStateException(); running = true; // Register to receive network status events networkStateReceiver = new NetworkStateReceiver(); diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 19499171b..cd460d344 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -51,6 +51,7 @@ import java.util.Map; import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -94,6 +95,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final File torDirectory, torFile, geoIpFile, configFile; private final File doneFile, cookieFile; private final PowerManager.WakeLock wakeLock; + private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; private volatile ServerSocket socket = null; @@ -130,19 +132,24 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { wakeLock.setReferenceCounted(false); } + @Override public TransportId getId() { return ID; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { return maxIdleTime; } + @Override public boolean start() throws IOException { + if (used.getAndSet(true)) throw new IllegalStateException(); // Try to connect to an existing Tor process if there is one boolean startProcess = false; try { @@ -369,6 +376,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void bind() { ioExecutor.execute(new Runnable() { + @Override public void run() { // If there's already a port number stored in config, reuse it String portString = callback.getSettings().get("port"); @@ -398,6 +406,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.mergeSettings(s); // Create a hidden service if necessary ioExecutor.execute(new Runnable() { + @Override public void run() { publishHiddenService(localPort); } @@ -486,6 +495,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + @Override public void stop() throws IOException { running = false; tryToClose(socket); @@ -508,18 +518,22 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { wakeLock.release(); } + @Override public boolean isRunning() { return running && connectionStatus.isConnected(); } + @Override public boolean shouldPoll() { return true; } + @Override public int getPollingInterval() { return backoff.getPollingInterval(); } + @Override public void poll(Collection connected) { if (!isRunning()) return; backoff.increment(); @@ -530,6 +544,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void connectAndCallBack(final ContactId c) { ioExecutor.execute(new Runnable() { + @Override public void run() { DuplexTransportConnection d = createConnection(c); if (d != null) { @@ -540,6 +555,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { }); } + @Override public DuplexTransportConnection createConnection(ContactId c) { if (!isRunning()) return null; TransportProperties p = callback.getRemoteProperties().get(c); @@ -566,29 +582,34 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + @Override public boolean supportsInvitations() { return false; } + @Override public DuplexTransportConnection createInvitationConnection(PseudoRandom r, long timeout, boolean alice) { throw new UnsupportedOperationException(); } + @Override public boolean supportsKeyAgreement() { return false; } - public KeyAgreementListener createKeyAgreementListener( - byte[] commitment) { + @Override + public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { throw new UnsupportedOperationException(); } + @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, TransportDescriptor d, long timeout) { throw new UnsupportedOperationException(); } + @Override public void circuitStatus(String status, String id, String path) { if (status.equals("BUILT") && connectionStatus.getAndSetCircuitBuilt()) { @@ -598,19 +619,24 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + @Override public void streamStatus(String status, String id, String target) { } + @Override public void orConnStatus(String status, String orName) { if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status); } + @Override public void bandwidthUsed(long read, long written) { } + @Override public void newDescriptors(List orList) { } + @Override public void message(String severity, String msg) { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { @@ -621,6 +647,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + @Override public void unrecognized(String type, String msg) { if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) LOG.info("Descriptor uploaded"); @@ -642,6 +669,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + @Override public void eventOccurred(Event e) { if (e instanceof SettingsUpdatedEvent) { if (((SettingsUpdatedEvent) e).getNamespace().equals("tor")) { @@ -653,6 +681,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void updateConnectionStatus() { ioExecutor.execute(new Runnable() { + @Override public void run() { if (!running) return; diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java index 3394995cf..981923553 100644 --- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java +++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java @@ -14,6 +14,7 @@ import java.io.OutputStream; import java.util.Collection; import java.util.Locale; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import static java.util.logging.Level.WARNING; @@ -27,6 +28,7 @@ public abstract class FilePlugin implements SimplexPlugin { protected final Executor ioExecutor; protected final SimplexPluginCallback callback; protected final int maxLatency; + protected final AtomicBoolean used = new AtomicBoolean(false); protected volatile boolean running = false; @@ -42,22 +44,27 @@ public abstract class FilePlugin implements SimplexPlugin { this.maxLatency = maxLatency; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { return Integer.MAX_VALUE; // We don't need keepalives } + @Override public boolean isRunning() { return running; } + @Override public TransportConnectionReader createReader(ContactId c) { return null; } + @Override public TransportConnectionWriter createWriter(ContactId c) { if (!running) return null; return createWriter(createConnectionFilename()); @@ -105,6 +112,7 @@ public abstract class FilePlugin implements SimplexPlugin { this.file = file; } + @Override public void run() { if (isPossibleConnectionFilename(file.getName())) { try { diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java index 8a75a971e..f50e96794 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java @@ -22,6 +22,7 @@ class LanTcpPlugin extends TcpPlugin { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime); } + @Override public TransportId getId() { return ID; } diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index d6effe2a7..fa3bd311a 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -42,6 +43,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected final Backoff backoff; protected final DuplexPluginCallback callback; protected final int maxLatency, maxIdleTime, socketTimeout; + protected final AtomicBoolean used = new AtomicBoolean(false); protected volatile boolean running = false; protected volatile ServerSocket socket = null; @@ -67,15 +69,19 @@ abstract class TcpPlugin implements DuplexPlugin { else socketTimeout = maxIdleTime * 2; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { return maxIdleTime; } + @Override public boolean start() { + if (used.getAndSet(true)) throw new IllegalStateException(); running = true; bind(); return true; @@ -83,6 +89,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected void bind() { ioExecutor.execute(new Runnable() { + @Override public void run() { if (!running) return; ServerSocket ss = null; @@ -158,23 +165,28 @@ abstract class TcpPlugin implements DuplexPlugin { } } + @Override public void stop() { running = false; tryToClose(socket); } + @Override public boolean isRunning() { return running && socket != null && !socket.isClosed(); } + @Override public boolean shouldPoll() { return true; } + @Override public int getPollingInterval() { return backoff.getPollingInterval(); } + @Override public void poll(Collection connected) { if (!isRunning()) return; backoff.increment(); @@ -185,6 +197,7 @@ abstract class TcpPlugin implements DuplexPlugin { private void connectAndCallBack(final ContactId c) { ioExecutor.execute(new Runnable() { + @Override public void run() { DuplexTransportConnection d = createConnection(c); if (d != null) { @@ -195,6 +208,7 @@ abstract class TcpPlugin implements DuplexPlugin { }); } + @Override public DuplexTransportConnection createConnection(ContactId c) { if (!isRunning()) return null; InetSocketAddress remote = getRemoteSocketAddress(c); @@ -243,24 +257,28 @@ abstract class TcpPlugin implements DuplexPlugin { } } + @Override public boolean supportsInvitations() { return false; } + @Override public DuplexTransportConnection createInvitationConnection(PseudoRandom r, long timeout, boolean alice) { throw new UnsupportedOperationException(); } + @Override public boolean supportsKeyAgreement() { return false; } - public KeyAgreementListener createKeyAgreementListener( - byte[] commitment) { + @Override + public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { throw new UnsupportedOperationException(); } + @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, TransportDescriptor d, long timeout) { throw new UnsupportedOperationException(); diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java index beabc8bec..cee16ac25 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java @@ -27,6 +27,7 @@ class WanTcpPlugin extends TcpPlugin { this.portMapper = portMapper; } + @Override public TransportId getId() { return ID; } diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java index 875112c5e..b8a0418c0 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java @@ -30,6 +30,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.bluetooth.BluetoothStateException; @@ -62,6 +63,7 @@ class BluetoothPlugin implements DuplexPlugin { private final DuplexPluginCallback callback; private final int maxLatency; private final Semaphore discoverySemaphore = new Semaphore(1); + private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; private volatile StreamConnectionNotifier socket = null; @@ -76,20 +78,25 @@ class BluetoothPlugin implements DuplexPlugin { this.maxLatency = maxLatency; } + @Override public TransportId getId() { return ID; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { // Bluetooth detects dead connections so we don't need keepalives return Integer.MAX_VALUE; } + @Override public boolean start() throws IOException { + if (used.getAndSet(true)) throw new IllegalStateException(); // Initialise the Bluetooth stack try { localDevice = LocalDevice.getLocalDevice(); @@ -108,6 +115,7 @@ class BluetoothPlugin implements DuplexPlugin { private void bind() { ioExecutor.execute(new Runnable() { + @Override public void run() { if (!running) return; // Advertise the Bluetooth address to contacts @@ -183,23 +191,28 @@ class BluetoothPlugin implements DuplexPlugin { return new BluetoothTransportConnection(this, s); } + @Override public void stop() { running = false; tryToClose(socket); } + @Override public boolean isRunning() { return running; } + @Override public boolean shouldPoll() { return true; } + @Override public int getPollingInterval() { return backoff.getPollingInterval(); } + @Override public void poll(final Collection connected) { if (!running) return; backoff.increment(); @@ -214,6 +227,7 @@ class BluetoothPlugin implements DuplexPlugin { final String uuid = e.getValue().get(PROP_UUID); if (StringUtils.isNullOrEmpty(uuid)) continue; ioExecutor.execute(new Runnable() { + @Override public void run() { if (!running) return; StreamConnection s = connect(makeUrl(address, uuid)); @@ -238,6 +252,7 @@ class BluetoothPlugin implements DuplexPlugin { } } + @Override public DuplexTransportConnection createConnection(ContactId c) { if (!running) return null; TransportProperties p = callback.getRemoteProperties().get(c); @@ -252,10 +267,12 @@ class BluetoothPlugin implements DuplexPlugin { return new BluetoothTransportConnection(this, s); } + @Override public boolean supportsInvitations() { return true; } + @Override public DuplexTransportConnection createInvitationConnection(PseudoRandom r, long timeout, boolean alice) { if (!running) return null; @@ -279,9 +296,8 @@ class BluetoothPlugin implements DuplexPlugin { } // Create the background tasks CompletionService complete = - new ExecutorCompletionService(ioExecutor); - List> futures = - new ArrayList>(); + new ExecutorCompletionService<>(ioExecutor); + List> futures = new ArrayList<>(); if (alice) { // Return the first connected socket futures.add(complete.submit(new ListeningTask(ss))); @@ -316,6 +332,7 @@ class BluetoothPlugin implements DuplexPlugin { private void closeSockets(final List> futures, final StreamConnection chosen) { ioExecutor.execute(new Runnable() { + @Override public void run() { for (Future f : futures) { try { @@ -331,9 +348,7 @@ class BluetoothPlugin implements DuplexPlugin { } catch (InterruptedException e) { LOG.info("Interrupted while closing sockets"); return; - } catch (ExecutionException e) { - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); - } catch (IOException e) { + } catch (ExecutionException | IOException e) { if (LOG.isLoggable(INFO)) LOG.info(e.toString()); } } @@ -341,14 +356,15 @@ class BluetoothPlugin implements DuplexPlugin { }); } + @Override public boolean supportsKeyAgreement() { return true; } - public KeyAgreementListener createKeyAgreementListener( - byte[] localCommitment) { + @Override + public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { // No truncation necessary because COMMIT_LENGTH = 16 - String uuid = UUID.nameUUIDFromBytes(localCommitment).toString(); + String uuid = UUID.nameUUIDFromBytes(commitment).toString(); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); String url = makeUrl("localhost", uuid); // Make the device discoverable if possible @@ -371,8 +387,9 @@ class BluetoothPlugin implements DuplexPlugin { return new BluetoothKeyAgreementListener(d, ss); } + @Override public DuplexTransportConnection createKeyAgreementConnection( - byte[] remoteCommitment, TransportDescriptor d, long timeout) { + byte[] commitment, TransportDescriptor d, long timeout) { if (!isRunning()) return null; if (!ID.equals(d.getIdentifier())) return null; TransportProperties p = d.getProperties(); @@ -380,7 +397,7 @@ class BluetoothPlugin implements DuplexPlugin { String address = p.get(PROP_ADDRESS); if (StringUtils.isNullOrEmpty(address)) return null; // No truncation necessary because COMMIT_LENGTH = 16 - String uuid = UUID.nameUUIDFromBytes(remoteCommitment).toString(); + String uuid = UUID.nameUUIDFromBytes(commitment).toString(); if (LOG.isLoggable(INFO)) LOG.info("Connecting to key agreement UUID " + uuid); String url = makeUrl(address, uuid); diff --git a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java index 116143225..926f8d314 100644 --- a/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/file/RemovableDrivePlugin.java @@ -34,29 +34,36 @@ implements RemovableDriveMonitor.Callback { this.monitor = monitor; } + @Override public TransportId getId() { return ID; } + @Override public boolean start() throws IOException { + if (used.getAndSet(true)) throw new IllegalStateException(); running = true; monitor.start(this); return true; } + @Override public void stop() throws IOException { running = false; monitor.stop(); } + @Override public boolean shouldPoll() { return false; } + @Override public int getPollingInterval() { throw new UnsupportedOperationException(); } + @Override public void poll(Collection connected) { throw new UnsupportedOperationException(); } @@ -64,8 +71,7 @@ implements RemovableDriveMonitor.Callback { @Override protected File chooseOutputDirectory() { try { - List drives = - new ArrayList(finder.findRemovableDrives()); + List drives = new ArrayList<>(finder.findRemovableDrives()); if (drives.isEmpty()) return null; String[] paths = new String[drives.size()]; for (int i = 0; i < paths.length; i++) { @@ -92,7 +98,7 @@ implements RemovableDriveMonitor.Callback { @Override protected Collection findFilesByName(String filename) { - List matches = new ArrayList(); + List matches = new ArrayList<>(); try { for (File drive : finder.findRemovableDrives()) { File[] files = drive.listFiles(); @@ -109,6 +115,7 @@ implements RemovableDriveMonitor.Callback { return Collections.unmodifiableList(matches); } + @Override public void driveInserted(File root) { File[] files = root.listFiles(); if (files != null) { @@ -116,6 +123,7 @@ implements RemovableDriveMonitor.Callback { } } + @Override public void exceptionThrown(IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java index 10a68aa56..ce2d2d57e 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import static java.util.logging.Level.INFO; @@ -32,6 +33,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private final SerialPortList serialPortList; private final DuplexPluginCallback callback; private final int maxLatency; + private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; private volatile Modem modem = null; @@ -44,20 +46,25 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { this.maxLatency = maxLatency; } + @Override public TransportId getId() { return ID; } + @Override public int getMaxLatency() { return maxLatency; } + @Override public int getMaxIdleTime() { // FIXME: Do we need keepalives for this transport? return Integer.MAX_VALUE; } + @Override public boolean start() { + if (used.getAndSet(true)) throw new IllegalStateException(); for (String portName : serialPortList.getPortNames()) { if (LOG.isLoggable(INFO)) LOG.info("Trying to initialise modem on " + portName); @@ -75,6 +82,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return false; } + @Override public void stop() { running = false; if (modem != null) { @@ -86,18 +94,22 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } } + @Override public boolean isRunning() { return running; } + @Override public boolean shouldPoll() { return false; } + @Override public int getPollingInterval() { throw new UnsupportedOperationException(); } + @Override public void poll(Collection connected) { throw new UnsupportedOperationException(); } @@ -121,6 +133,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return false; } + @Override public DuplexTransportConnection createConnection(ContactId c) { if (!running) return null; // Get the ISO 3166 code for the caller's country @@ -148,29 +161,34 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return new ModemTransportConnection(); } + @Override public boolean supportsInvitations() { return false; } + @Override public DuplexTransportConnection createInvitationConnection(PseudoRandom r, long timeout, boolean alice) { throw new UnsupportedOperationException(); } + @Override public boolean supportsKeyAgreement() { return false; } - public KeyAgreementListener createKeyAgreementListener( - byte[] commitment) { + @Override + public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { throw new UnsupportedOperationException(); } + @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, TransportDescriptor d, long timeout) { throw new UnsupportedOperationException(); } + @Override public void incomingCallConnected() { LOG.info("Incoming call connected"); callback.incomingConnectionCreated(new ModemTransportConnection()); From e36d1c89548e5e93dd8df8eaac4ddab3341688f1 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 6 May 2016 09:21:27 +0100 Subject: [PATCH 3/5] Fixed possible NPE from uninitialised control connection. --- briar-android/src/org/briarproject/plugins/tor/TorPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index cd460d344..a09af4724 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -217,13 +217,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { // Now we should be able to connect to the new process controlSocket = new Socket("127.0.0.1", CONTROL_PORT); } - running = true; // Open a control connection and authenticate using the cookie file controlConnection = new TorControlConnection(controlSocket); controlConnection.authenticate(read(cookieFile)); // Tell Tor to exit when the control connection is closed controlConnection.takeOwnership(); controlConnection.resetConf(Collections.singletonList(OWNER)); + running = true; // Register to receive events from the Tor process controlConnection.setEventHandler(this); controlConnection.setEvents(Arrays.asList(EVENTS)); From 86f409d0bfa164986ac5e6a6c21aa072d5cb4c5b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 6 May 2016 09:22:34 +0100 Subject: [PATCH 4/5] Don't send dev reports until transport is enabled. --- .../org/briarproject/plugins/tor/TorPlugin.java | 14 +++++++++----- .../briarproject/api/reporting/DevReporter.java | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index a09af4724..0f7aaf65c 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -233,7 +233,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (phase != null && phase.contains("PROGRESS=100")) { LOG.info("Tor has already bootstrapped"); connectionStatus.setBootstrapped(); - sendDevReports(); } } // Register to receive network status events @@ -615,7 +614,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { connectionStatus.getAndSetCircuitBuilt()) { LOG.info("First circuit built"); backoff.reset(); - if (isRunning()) callback.transportEnabled(); + if (isRunning()) { + sendDevReports(); + callback.transportEnabled(); + } } } @@ -641,9 +643,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { connectionStatus.setBootstrapped(); - sendDevReports(); backoff.reset(); - if (isRunning()) callback.transportEnabled(); + if (isRunning()) { + sendDevReports(); + callback.transportEnabled(); + } } } @@ -688,7 +692,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { Object o = appContext.getSystemService(CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) o; NetworkInfo net = cm.getActiveNetworkInfo(); - boolean online = net != null && net.isConnected(); + boolean online = net != null && net.isConnected(); boolean wifi = online && net.getType() == TYPE_WIFI; String country = locationUtils.getCurrentCountry(); boolean blocked = TorNetworkMetadata.isTorProbablyBlocked( diff --git a/briar-api/src/org/briarproject/api/reporting/DevReporter.java b/briar-api/src/org/briarproject/api/reporting/DevReporter.java index f4b17820c..7c0f14aa0 100644 --- a/briar-api/src/org/briarproject/api/reporting/DevReporter.java +++ b/briar-api/src/org/briarproject/api/reporting/DevReporter.java @@ -2,7 +2,6 @@ package org.briarproject.api.reporting; import java.io.File; import java.io.FileNotFoundException; -import java.io.IOException; /** * A task for reporting back to the developers. From ef19f2ec48b2c3cb5441a5010ebafe2949203061 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 6 May 2016 09:49:15 +0100 Subject: [PATCH 5/5] Start plugins asynchronously. This allows the lifecycle manager to continue starting other services while plugins are starting, and allows the plugin manager to stop each plugin as soon as it has started. --- .../lifecycle/LifecycleManagerImpl.java | 23 +++--- .../plugins/PluginManagerImpl.java | 72 ++++++++++--------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java b/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java index b22794311..3abfa4cbc 100644 --- a/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java +++ b/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java @@ -53,20 +53,20 @@ class LifecycleManagerImpl implements LifecycleManager { @Override public void registerService(Service s) { if (LOG.isLoggable(INFO)) - LOG.info("Registering service " + s.getClass().getName()); + LOG.info("Registering service " + s.getClass().getSimpleName()); services.add(s); } @Override public void registerClient(Client c) { if (LOG.isLoggable(INFO)) - LOG.info("Registering client " + c.getClass().getName()); + LOG.info("Registering client " + c.getClass().getSimpleName()); clients.add(c); } @Override public void registerForShutdown(ExecutorService e) { - LOG.info("Registering executor"); + LOG.info("Registering executor " + e.getClass().getSimpleName()); executors.add(e); } @@ -94,7 +94,8 @@ class LifecycleManagerImpl implements LifecycleManager { c.createLocalState(txn); duration = System.currentTimeMillis() - start; if (LOG.isLoggable(INFO)) { - LOG.info("Starting client " + c.getClass().getName() + LOG.info("Starting client " + + c.getClass().getSimpleName() + " took " + duration + " ms"); } } @@ -107,7 +108,7 @@ class LifecycleManagerImpl implements LifecycleManager { s.startService(); duration = System.currentTimeMillis() - start; if (LOG.isLoggable(INFO)) { - LOG.info("Starting service " + s.getClass().getName() + LOG.info("Starting service " + s.getClass().getSimpleName() + " took " + duration + " ms"); } } @@ -140,13 +141,17 @@ class LifecycleManagerImpl implements LifecycleManager { s.stopService(); long duration = System.currentTimeMillis() - start; if (LOG.isLoggable(INFO)) { - LOG.info("Stopping service " + s.getClass().getName() + LOG.info("Stopping service " + s.getClass().getSimpleName() + " took " + duration + " ms"); } } - for (ExecutorService e : executors) e.shutdownNow(); - if (LOG.isLoggable(INFO)) - LOG.info(executors.size() + " executors shut down"); + for (ExecutorService e : executors) { + if (LOG.isLoggable(INFO)) { + LOG.info("Stopping executor " + + e.getClass().getSimpleName()); + } + e.shutdownNow(); + } long start = System.currentTimeMillis(); db.close(); long duration = System.currentTimeMillis() - start; diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 852fe3dc9..e8b9b129d 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -62,6 +62,7 @@ class PluginManagerImpl implements PluginManager, Service { private final Map plugins; private final List simplexPlugins; private final List duplexPlugins; + private final Map startLatches; private final AtomicBoolean used = new AtomicBoolean(false); @Inject @@ -80,69 +81,64 @@ class PluginManagerImpl implements PluginManager, Service { plugins = new ConcurrentHashMap(); simplexPlugins = new CopyOnWriteArrayList(); duplexPlugins = new CopyOnWriteArrayList(); + startLatches = new ConcurrentHashMap(); } @Override public void startService() throws ServiceException { if (used.getAndSet(true)) throw new IllegalStateException(); - Collection simplexFactories = - pluginConfig.getSimplexFactories(); - Collection duplexFactories = - pluginConfig.getDuplexFactories(); - int numPlugins = simplexFactories.size() + duplexFactories.size(); - CountDownLatch latch = new CountDownLatch(numPlugins); - // Instantiate and start the simplex plugins + // Instantiate the simplex plugins and start them asynchronously LOG.info("Starting simplex plugins"); - for (SimplexPluginFactory f : simplexFactories) { + for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) { TransportId t = f.getId(); SimplexPlugin s = f.createPlugin(new SimplexCallback(t)); if (s == null) { if (LOG.isLoggable(WARNING)) LOG.warning("Could not create plugin for " + t); - latch.countDown(); } else { plugins.put(t, s); simplexPlugins.add(s); - ioExecutor.execute(new PluginStarter(s, latch)); + CountDownLatch startLatch = new CountDownLatch(1); + startLatches.put(t, startLatch); + ioExecutor.execute(new PluginStarter(s, startLatch)); } } - // Instantiate and start the duplex plugins + // Instantiate the duplex plugins and start them asynchronously LOG.info("Starting duplex plugins"); - for (DuplexPluginFactory f : duplexFactories) { + for (DuplexPluginFactory f : pluginConfig.getDuplexFactories()) { TransportId t = f.getId(); DuplexPlugin d = f.createPlugin(new DuplexCallback(t)); if (d == null) { if (LOG.isLoggable(WARNING)) LOG.warning("Could not create plugin for " + t); - latch.countDown(); } else { plugins.put(t, d); duplexPlugins.add(d); - ioExecutor.execute(new PluginStarter(d, latch)); + CountDownLatch startLatch = new CountDownLatch(1); + startLatches.put(t, startLatch); + ioExecutor.execute(new PluginStarter(d, startLatch)); } } - // Wait for all the plugins to start - try { - latch.await(); - } catch (InterruptedException e) { - throw new ServiceException(e); - } } @Override public void stopService() throws ServiceException { - CountDownLatch latch = new CountDownLatch(plugins.size()); + CountDownLatch stopLatch = new CountDownLatch(plugins.size()); // Stop the simplex plugins LOG.info("Stopping simplex plugins"); - for (SimplexPlugin plugin : simplexPlugins) - ioExecutor.execute(new PluginStopper(plugin, latch)); + for (SimplexPlugin s : simplexPlugins) { + CountDownLatch startLatch = startLatches.get(s.getId()); + ioExecutor.execute(new PluginStopper(s, startLatch, stopLatch)); + } // Stop the duplex plugins LOG.info("Stopping duplex plugins"); - for (DuplexPlugin plugin : duplexPlugins) - ioExecutor.execute(new PluginStopper(plugin, latch)); + for (DuplexPlugin d : duplexPlugins) { + CountDownLatch startLatch = startLatches.get(d.getId()); + ioExecutor.execute(new PluginStopper(d, startLatch, stopLatch)); + } // Wait for all the plugins to stop try { - latch.await(); + stopLatch.await(); } catch (InterruptedException e) { throw new ServiceException(e); } @@ -182,11 +178,11 @@ class PluginManagerImpl implements PluginManager, Service { private class PluginStarter implements Runnable { private final Plugin plugin; - private final CountDownLatch latch; + private final CountDownLatch startLatch; - private PluginStarter(Plugin plugin, CountDownLatch latch) { + private PluginStarter(Plugin plugin, CountDownLatch startLatch) { this.plugin = plugin; - this.latch = latch; + this.startLatch = startLatch; } @Override @@ -212,7 +208,7 @@ class PluginManagerImpl implements PluginManager, Service { LOG.log(WARNING, e.toString(), e); } } finally { - latch.countDown(); + startLatch.countDown(); } } } @@ -220,16 +216,21 @@ class PluginManagerImpl implements PluginManager, Service { private class PluginStopper implements Runnable { private final Plugin plugin; - private final CountDownLatch latch; + private final CountDownLatch startLatch, stopLatch; - private PluginStopper(Plugin plugin, CountDownLatch latch) { + private PluginStopper(Plugin plugin, CountDownLatch startLatch, + CountDownLatch stopLatch) { this.plugin = plugin; - this.latch = latch; + this.startLatch = startLatch; + this.stopLatch = stopLatch; } @Override public void run() { try { + // Wait for the plugin to finish starting + startLatch.await(); + // Stop the plugin long start = System.currentTimeMillis(); plugin.stop(); long duration = System.currentTimeMillis() - start; @@ -237,10 +238,13 @@ class PluginManagerImpl implements PluginManager, Service { LOG.info("Stopping plugin " + plugin.getId() + " took " + duration + " ms"); } + } catch (InterruptedException e) { + LOG.warning("Interrupted while waiting for plugin to start"); + // This task runs on an executor, so don't reset the interrupt } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } finally { - latch.countDown(); + stopLatch.countDown(); } } }