From 8fa77b57da737abd6ee542eb954c02713bfad84f Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Thu, 6 Oct 2011 19:33:12 +0100
Subject: [PATCH] Bluetooth plugin (untested).

---
 .../plugins/bluetooth/BluetoothPlugin.java    | 216 ++++++++++++++++++
 .../BluetoothTransportConnection.java         |  30 +++
 2 files changed, 246 insertions(+)
 create mode 100644 components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
 create mode 100644 components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java

diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
new file mode 100644
index 0000000000..a2a6e3083a
--- /dev/null
+++ b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java
@@ -0,0 +1,216 @@
+package net.sf.briar.plugins.bluetooth;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+
+import javax.bluetooth.BluetoothStateException;
+import javax.bluetooth.DiscoveryAgent;
+import javax.bluetooth.LocalDevice;
+import javax.microedition.io.Connector;
+import javax.microedition.io.StreamConnection;
+import javax.microedition.io.StreamConnectionNotifier;
+
+import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportId;
+import net.sf.briar.api.transport.InvalidConfigException;
+import net.sf.briar.api.transport.InvalidPropertiesException;
+import net.sf.briar.api.transport.stream.StreamTransportCallback;
+import net.sf.briar.api.transport.stream.StreamTransportConnection;
+import net.sf.briar.api.transport.stream.StreamTransportPlugin;
+import net.sf.briar.plugins.AbstractPlugin;
+
+class BluetoothPlugin extends AbstractPlugin implements StreamTransportPlugin {
+
+	public static final int TRANSPORT_ID = 2;
+
+	private static final TransportId id = new TransportId(TRANSPORT_ID);
+
+	private final String uuid;
+	private final long pollingInterval;
+
+	private StreamTransportCallback callback = null;
+	private StreamConnectionNotifier streamConnectionNotifier = null;
+
+	BluetoothPlugin(Executor executor, String uuid, long pollingInterval) {
+		super(executor);
+		this.uuid = uuid;
+		this.pollingInterval = pollingInterval;
+	}
+
+	public TransportId getId() {
+		return id;
+	}
+
+	public synchronized void start(Map<String, String> localProperties,
+			Map<ContactId, Map<String, String>> remoteProperties,
+			Map<String, String> config, StreamTransportCallback callback)
+	throws InvalidPropertiesException, InvalidConfigException,
+	IOException {
+		super.start(localProperties, remoteProperties, config);
+		this.callback = callback;
+		executor.execute(createBinder());
+	}
+
+	public synchronized void stop() throws IOException {
+		super.stop();
+		if(streamConnectionNotifier != null) {
+			streamConnectionNotifier.close();
+			streamConnectionNotifier = null;
+		}
+	}
+
+	private Runnable createBinder() {
+		return new Runnable() {
+			public void run() {
+				bind();
+			}
+		};
+	}
+
+	private void bind() {
+		synchronized(this) {
+			if(!started) return;
+		}
+		// Initialise the Bluetooth stack
+		LocalDevice localDevice = null;
+		try {
+			localDevice = LocalDevice.getLocalDevice();
+		} catch(BluetoothStateException e) {
+			// FIXME: Logging
+			return;
+		}
+		// Try to make the device discoverable (requires root on Linux)
+		try {
+			localDevice.setDiscoverable(DiscoveryAgent.GIAC);
+		} catch(BluetoothStateException e) {
+			// FIXME: Logging
+			try {
+				localDevice.setDiscoverable(DiscoveryAgent.LIAC);
+			} catch(BluetoothStateException e1) {
+				// FIXME: Logging
+			}
+		}
+		// Bind the port
+		String url = "btspp://localhost:" + uuid + ";name=" + uuid;
+		StreamConnectionNotifier scn;
+		try {
+			scn = (StreamConnectionNotifier) Connector.open(url);
+		} catch(IOException e) {
+			// FIXME: Logging
+			return;
+		}
+		synchronized(this) {
+			if(!started) return;
+			streamConnectionNotifier = scn;
+			setConnectionUrl(localDevice.getBluetoothAddress());
+			startListener();
+		}
+	}
+
+	private void startListener() {
+		new Thread() {
+			@Override
+			public void run() {
+				listen();
+			}
+		}.start();
+	}
+
+	private void listen() {
+		while(true) {
+			StreamConnectionNotifier scn;
+			StreamConnection s;
+			synchronized(this) {
+				if(!started) return;
+				scn = streamConnectionNotifier;
+			}
+			try {
+				s = scn.acceptAndOpen();
+			} catch(IOException e) {
+				// FIXME: Logging
+				return;
+			}
+			synchronized(this) {
+				if(!started) {
+					try {
+						s.close();
+					} catch(IOException e) {
+						// FIXME: Logging
+					}
+					return;
+				}
+				BluetoothTransportConnection conn =
+					new BluetoothTransportConnection(s);
+				callback.incomingConnectionCreated(conn);
+			}
+		}
+	}
+
+	private void setConnectionUrl(String address) {
+		// Update the local properties with the connection URL
+		String url = "btspp://" + address + ":" + uuid + ";name=" + uuid;
+		Map<String, String> m = new TreeMap<String, String>(localProperties);
+		m.put("url", url);
+		callback.setLocalProperties(m);
+	}
+
+	public boolean shouldPoll() {
+		return true;
+	}
+
+	public long getPollingInterval() {
+		return pollingInterval;
+	}
+
+	public synchronized void poll() {
+		if(!started) return;
+		for(ContactId c : remoteProperties.keySet()) {
+			executor.execute(createConnector(c));
+		}
+	}
+
+	private Runnable createConnector(final ContactId c) {
+		return new Runnable() {
+			public void run() {
+				connect(c);
+			}
+		};
+	}
+
+	private StreamTransportConnection connect(ContactId c) {
+		StreamTransportConnection conn = createAndConnectSocket(c);
+		if(conn != null) {
+			synchronized(this) {
+				if(started) callback.outgoingConnectionCreated(c, conn);
+			}
+		}
+		return conn;
+	}
+
+	private StreamTransportConnection createAndConnectSocket(ContactId c) {
+		try {
+			String url;
+			synchronized(this) {
+				if(!started) return null;
+				Map<String, String> properties = remoteProperties.get(c);
+				if(properties == null) return null;
+				url = properties.get("url");
+				if(url == null) return null;
+			}
+			StreamConnection s = (StreamConnection) Connector.open(url);
+			return new BluetoothTransportConnection(s);
+		} catch(IOException e) {
+			// FIXME: Logging
+			return null;
+		}
+	}
+
+	public StreamTransportConnection createConnection(ContactId c) {
+		synchronized(this) {
+			if(!started) return null;
+		}
+		return createAndConnectSocket(c);
+	}
+}
diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java b/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java
new file mode 100644
index 0000000000..dc2f666dcf
--- /dev/null
+++ b/components/net/sf/briar/plugins/bluetooth/BluetoothTransportConnection.java
@@ -0,0 +1,30 @@
+package net.sf.briar.plugins.bluetooth;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.microedition.io.StreamConnection;
+
+import net.sf.briar.api.transport.stream.StreamTransportConnection;
+
+class BluetoothTransportConnection implements StreamTransportConnection {
+
+	private final StreamConnection stream;
+
+	BluetoothTransportConnection(StreamConnection stream) {
+		this.stream = stream;
+	}
+
+	public InputStream getInputStream() throws IOException {
+		return stream.openInputStream();
+	}
+
+	public OutputStream getOutputStream() throws IOException {
+		return stream.openOutputStream();
+	}
+
+	public void dispose(boolean success) throws IOException {
+		stream.close();
+	}
+}
-- 
GitLab