mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 13:49:53 +01:00
Avoid making alien calls with locks held.
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
package net.sf.briar.plugins.bluetooth;
|
package net.sf.briar.plugins.bluetooth;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@@ -48,8 +47,9 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
private final StreamPluginCallback callback;
|
private final StreamPluginCallback callback;
|
||||||
private final long pollingInterval;
|
private final long pollingInterval;
|
||||||
private final Object discoveryLock = new Object();
|
private final Object discoveryLock = new Object();
|
||||||
|
private final Object localPropertiesLock = new Object();
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Set<StreamConnectionNotifier> sockets; // Locking: this
|
private final Collection<StreamConnectionNotifier> sockets; // Locking: this
|
||||||
|
|
||||||
private boolean running = false; // Locking: this
|
private boolean running = false; // Locking: this
|
||||||
private LocalDevice localDevice = null; // Locking: this
|
private LocalDevice localDevice = null; // Locking: this
|
||||||
@@ -61,7 +61,7 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.pollingInterval = pollingInterval;
|
this.pollingInterval = pollingInterval;
|
||||||
scheduler = Executors.newScheduledThreadPool(0);
|
scheduler = Executors.newScheduledThreadPool(0);
|
||||||
sockets = new HashSet<StreamConnectionNotifier>();
|
sockets = new ArrayList<StreamConnectionNotifier>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportId getId() {
|
public TransportId getId() {
|
||||||
@@ -70,19 +70,21 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
|
|
||||||
public void start() throws IOException {
|
public void start() throws IOException {
|
||||||
// Initialise the Bluetooth stack
|
// Initialise the Bluetooth stack
|
||||||
|
LocalDevice localDevice;
|
||||||
try {
|
try {
|
||||||
synchronized(this) {
|
localDevice = LocalDevice.getLocalDevice();
|
||||||
running = true;
|
|
||||||
localDevice = LocalDevice.getLocalDevice();
|
|
||||||
if(LOG.isLoggable(Level.INFO))
|
|
||||||
LOG.info("Address " + localDevice.getBluetoothAddress());
|
|
||||||
}
|
|
||||||
} catch(UnsatisfiedLinkError e) {
|
} catch(UnsatisfiedLinkError e) {
|
||||||
// On Linux the user may need to install libbluetooth-dev
|
// On Linux the user may need to install libbluetooth-dev
|
||||||
if(OsUtils.isLinux())
|
if(OsUtils.isLinux())
|
||||||
callback.showMessage("BLUETOOTH_INSTALL_LIBS");
|
callback.showMessage("BLUETOOTH_INSTALL_LIBS");
|
||||||
throw new IOException(e.toString());
|
throw new IOException(e.toString());
|
||||||
}
|
}
|
||||||
|
if(LOG.isLoggable(Level.INFO))
|
||||||
|
LOG.info("Address " + localDevice.getBluetoothAddress());
|
||||||
|
synchronized(this) {
|
||||||
|
running = true;
|
||||||
|
this.localDevice = localDevice;
|
||||||
|
}
|
||||||
pluginExecutor.execute(new Runnable() {
|
pluginExecutor.execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
bind();
|
bind();
|
||||||
@@ -91,13 +93,11 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
String uuid;
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!running) return;
|
if(!running) return;
|
||||||
uuid = getUuid();
|
|
||||||
makeDeviceDiscoverable();
|
|
||||||
}
|
}
|
||||||
String url = "btspp://localhost:" + uuid + ";name=RFCOMM";
|
makeDeviceDiscoverable();
|
||||||
|
String url = "btspp://localhost:" + getUuid() + ";name=RFCOMM";
|
||||||
StreamConnectionNotifier scn;
|
StreamConnectionNotifier scn;
|
||||||
try {
|
try {
|
||||||
scn = (StreamConnectionNotifier) Connector.open(url);
|
scn = (StreamConnectionNotifier) Connector.open(url);
|
||||||
@@ -115,24 +115,30 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
acceptContactConnections(scn);
|
acceptContactConnections(scn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized String getUuid() {
|
private String getUuid() {
|
||||||
assert running;
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
TransportProperties p = callback.getLocalProperties();
|
synchronized(localPropertiesLock) {
|
||||||
String uuid = p.get("uuid");
|
TransportProperties p = callback.getLocalProperties();
|
||||||
if(uuid == null) {
|
String uuid = p.get("uuid");
|
||||||
// Generate a (weakly) random UUID and store it
|
if(uuid == null) {
|
||||||
byte[] b = new byte[16];
|
// Generate a (weakly) random UUID and store it
|
||||||
new Random().nextBytes(b);
|
byte[] b = new byte[16];
|
||||||
uuid = StringUtils.toHexString(b);
|
new Random().nextBytes(b);
|
||||||
p.put("uuid", uuid);
|
uuid = StringUtils.toHexString(b);
|
||||||
callback.setLocalProperties(p);
|
p.put("uuid", uuid);
|
||||||
|
callback.setLocalProperties(p);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
}
|
}
|
||||||
return uuid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void makeDeviceDiscoverable() {
|
private void makeDeviceDiscoverable() {
|
||||||
assert running;
|
|
||||||
// Try to make the device discoverable (requires root on Linux)
|
// Try to make the device discoverable (requires root on Linux)
|
||||||
|
LocalDevice localDevice;
|
||||||
|
synchronized(this) {
|
||||||
|
if(!running) return;
|
||||||
|
localDevice = this.localDevice;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
localDevice.setDiscoverable(DiscoveryAgent.GIAC);
|
localDevice.setDiscoverable(DiscoveryAgent.GIAC);
|
||||||
} catch(BluetoothStateException e) {
|
} catch(BluetoothStateException e) {
|
||||||
@@ -140,9 +146,12 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
// Advertise the address to contacts if the device is discoverable
|
// Advertise the address to contacts if the device is discoverable
|
||||||
if(localDevice.getDiscoverable() != DiscoveryAgent.NOT_DISCOVERABLE) {
|
if(localDevice.getDiscoverable() != DiscoveryAgent.NOT_DISCOVERABLE) {
|
||||||
TransportProperties p = callback.getLocalProperties();
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
p.put("address", localDevice.getBluetoothAddress());
|
synchronized(localPropertiesLock) {
|
||||||
callback.setLocalProperties(p);
|
TransportProperties p = callback.getLocalProperties();
|
||||||
|
p.put("address", localDevice.getBluetoothAddress());
|
||||||
|
callback.setLocalProperties(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,16 +183,18 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
public void stop() {
|
||||||
running = false;
|
synchronized(this) {
|
||||||
localDevice = null;
|
running = false;
|
||||||
scheduler.shutdownNow();
|
localDevice = null;
|
||||||
for(StreamConnectionNotifier scn : sockets) tryToClose(scn);
|
for(StreamConnectionNotifier scn : sockets) tryToClose(scn);
|
||||||
sockets.clear();
|
sockets.clear();
|
||||||
if(socket != null) {
|
if(socket != null) {
|
||||||
tryToClose(socket);
|
tryToClose(socket);
|
||||||
socket = null;
|
socket = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
scheduler.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
@@ -224,11 +235,12 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
|
|
||||||
private Map<ContactId, String> discoverContactUrls(
|
private Map<ContactId, String> discoverContactUrls(
|
||||||
Map<ContactId, TransportProperties> remote) {
|
Map<ContactId, TransportProperties> remote) {
|
||||||
DiscoveryAgent discoveryAgent;
|
LocalDevice localDevice;
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!running) return Collections.emptyMap();
|
if(!running) return Collections.emptyMap();
|
||||||
discoveryAgent = localDevice.getDiscoveryAgent();
|
localDevice = this.localDevice;
|
||||||
}
|
}
|
||||||
|
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
|
||||||
Map<String, ContactId> addresses = new HashMap<String, ContactId>();
|
Map<String, ContactId> addresses = new HashMap<String, ContactId>();
|
||||||
Map<ContactId, String> uuids = new HashMap<ContactId, String>();
|
Map<ContactId, String> uuids = new HashMap<ContactId, String>();
|
||||||
for(Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
for(Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
@@ -245,6 +257,7 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
ContactListener listener = new ContactListener(discoveryAgent,
|
ContactListener listener = new ContactListener(discoveryAgent,
|
||||||
Collections.unmodifiableMap(addresses),
|
Collections.unmodifiableMap(addresses),
|
||||||
Collections.unmodifiableMap(uuids));
|
Collections.unmodifiableMap(uuids));
|
||||||
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
synchronized(discoveryLock) {
|
synchronized(discoveryLock) {
|
||||||
try {
|
try {
|
||||||
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
||||||
@@ -335,17 +348,19 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createInvitationConnection(ConnectionCallback c) {
|
private void createInvitationConnection(ConnectionCallback c) {
|
||||||
DiscoveryAgent discoveryAgent;
|
LocalDevice localDevice;
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!running) return;
|
if(!running) return;
|
||||||
discoveryAgent = localDevice.getDiscoveryAgent();
|
localDevice = this.localDevice;
|
||||||
}
|
}
|
||||||
|
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
|
||||||
// Try to discover the other party until the invitation times out
|
// Try to discover the other party until the invitation times out
|
||||||
long end = System.currentTimeMillis() + c.getTimeout();
|
long end = System.currentTimeMillis() + c.getTimeout();
|
||||||
String url = null;
|
String url = null;
|
||||||
while(url == null && System.currentTimeMillis() < end) {
|
while(url == null && System.currentTimeMillis() < end) {
|
||||||
InvitationListener listener = new InvitationListener(discoveryAgent,
|
InvitationListener listener = new InvitationListener(discoveryAgent,
|
||||||
c.getUuid());
|
c.getUuid());
|
||||||
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
synchronized(discoveryLock) {
|
synchronized(discoveryLock) {
|
||||||
try {
|
try {
|
||||||
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, listener);
|
||||||
@@ -378,8 +393,8 @@ class BluetoothPlugin implements StreamPlugin {
|
|||||||
private void bindInvitationSocket(final ConnectionCallback c) {
|
private void bindInvitationSocket(final ConnectionCallback c) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if(!running) return;
|
if(!running) return;
|
||||||
makeDeviceDiscoverable();
|
|
||||||
}
|
}
|
||||||
|
makeDeviceDiscoverable();
|
||||||
String url = "btspp://localhost:" + c.getUuid() + ";name=RFCOMM";
|
String url = "btspp://localhost:" + c.getUuid() + ";name=RFCOMM";
|
||||||
final StreamConnectionNotifier scn;
|
final StreamConnectionNotifier scn;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ abstract class FilePlugin implements BatchPlugin {
|
|||||||
private BatchTransportReader createInvitationReader(String filename,
|
private BatchTransportReader createInvitationReader(String filename,
|
||||||
long timeout) {
|
long timeout) {
|
||||||
Collection<File> files;
|
Collection<File> files;
|
||||||
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
synchronized(listenerLock) {
|
synchronized(listenerLock) {
|
||||||
// Find any matching files that have already arrived
|
// Find any matching files that have already arrived
|
||||||
files = findFilesByName(filename);
|
files = findFilesByName(filename);
|
||||||
@@ -170,6 +171,7 @@ abstract class FilePlugin implements BatchPlugin {
|
|||||||
public void run() {
|
public void run() {
|
||||||
String filename = file.getName();
|
String filename = file.getName();
|
||||||
if(isPossibleInvitationFilename(filename)) {
|
if(isPossibleInvitationFilename(filename)) {
|
||||||
|
// FIXME: Avoid making alien calls with a lock held
|
||||||
synchronized(listenerLock) {
|
synchronized(listenerLock) {
|
||||||
if(listener != null) listener.addFile(file);
|
if(listener != null) listener.addFile(file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,24 +30,31 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
|
|||||||
this.pollingInterval = pollingInterval;
|
this.pollingInterval = pollingInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void start(Callback callback) throws IOException {
|
public void start(Callback callback) throws IOException {
|
||||||
if(running) throw new IllegalStateException();
|
synchronized(this) {
|
||||||
running = true;
|
assert !running;
|
||||||
this.callback = callback;
|
assert this.callback == null;
|
||||||
|
assert exception == null;
|
||||||
|
running = true;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
pluginExecutor.execute(this);
|
pluginExecutor.execute(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
if(!running) throw new IllegalStateException();
|
IOException e;
|
||||||
running = false;
|
synchronized(this) {
|
||||||
if(exception != null) {
|
assert running;
|
||||||
IOException e = exception;
|
assert callback != null;
|
||||||
|
running = false;
|
||||||
|
callback = null;
|
||||||
|
e = exception;
|
||||||
exception = null;
|
exception = null;
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
synchronized(pollingLock) {
|
synchronized(pollingLock) {
|
||||||
pollingLock.notifyAll();
|
pollingLock.notifyAll();
|
||||||
}
|
}
|
||||||
|
if(e != null) throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -19,28 +19,42 @@ JNotifyListener {
|
|||||||
|
|
||||||
protected abstract String[] getPathsToWatch();
|
protected abstract String[] getPathsToWatch();
|
||||||
|
|
||||||
public synchronized void start(Callback callback) throws IOException {
|
public void start(Callback callback) throws IOException {
|
||||||
if(started) throw new IllegalStateException();
|
List<Integer> watches = new ArrayList<Integer>();
|
||||||
started = true;
|
|
||||||
this.callback = callback;
|
|
||||||
int mask = JNotify.FILE_CREATED;
|
int mask = JNotify.FILE_CREATED;
|
||||||
for(String path : getPathsToWatch()) {
|
for(String path : getPathsToWatch()) {
|
||||||
if(new File(path).exists())
|
if(new File(path).exists())
|
||||||
watches.add(JNotify.addWatch(path, mask, false, this));
|
watches.add(JNotify.addWatch(path, mask, false, this));
|
||||||
}
|
}
|
||||||
|
synchronized(this) {
|
||||||
|
assert !started;
|
||||||
|
assert callback == null;
|
||||||
|
started = true;
|
||||||
|
this.callback = callback;
|
||||||
|
this.watches.addAll(watches);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
if(!started) throw new IllegalStateException();
|
List<Integer> watches;
|
||||||
started = false;
|
synchronized(this) {
|
||||||
callback = null;
|
assert started;
|
||||||
|
assert callback != null;
|
||||||
|
started = false;
|
||||||
|
callback = null;
|
||||||
|
watches = new ArrayList<Integer>(this.watches);
|
||||||
|
this.watches.clear();
|
||||||
|
}
|
||||||
for(Integer w : watches) JNotify.removeWatch(w);
|
for(Integer w : watches) JNotify.removeWatch(w);
|
||||||
watches.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void fileCreated(int wd, String rootPath, String name) {
|
public void fileCreated(int wd, String rootPath, String name) {
|
||||||
if(!started) throw new IllegalStateException();
|
Callback callback;
|
||||||
callback.driveInserted(new File(rootPath + "/" + name));
|
synchronized(this) {
|
||||||
|
callback = this.callback;
|
||||||
|
}
|
||||||
|
if(callback != null)
|
||||||
|
callback.driveInserted(new File(rootPath + "/" + name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fileDeleted(int wd, String rootPath, String name) {
|
public void fileDeleted(int wd, String rootPath, String name) {
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ abstract class SocketPlugin implements StreamPlugin {
|
|||||||
public synchronized void stop() throws IOException {
|
public synchronized void stop() throws IOException {
|
||||||
running = false;
|
running = false;
|
||||||
if(socket != null) {
|
if(socket != null) {
|
||||||
socket.close();
|
tryToClose(socket);
|
||||||
socket = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user