mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Modem implementation using jSSC (untested).
This commit is contained in:
BIN
libs/jssc-0.9-briar.jar
Normal file
BIN
libs/jssc-0.9-briar.jar
Normal file
Binary file not shown.
35
src/net/sf/briar/plugins/modem/Modem.java
Normal file
35
src/net/sf/briar/plugins/modem/Modem.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package net.sf.briar.plugins.modem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
interface Modem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this method once after creating the modem. If an exception is
|
||||||
|
* thrown, the modem cannot be used.
|
||||||
|
*/
|
||||||
|
void init() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates an outgoing call and returns true if the call connects. If the
|
||||||
|
* call does not connect the modem is hung up.
|
||||||
|
*/
|
||||||
|
boolean dial(String number) throws IOException;
|
||||||
|
|
||||||
|
/** Returns a stream for reading from the currently connected call. */
|
||||||
|
InputStream getInputStream();
|
||||||
|
|
||||||
|
/** Returns a stream for writing to the currently connected call. */
|
||||||
|
OutputStream getOutputStream();
|
||||||
|
|
||||||
|
/** Hangs up the modem, ending the currently connected call. */
|
||||||
|
void hangUp() throws IOException;
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
|
||||||
|
/** Called when an incoming call connects. */
|
||||||
|
void incomingCallConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
304
src/net/sf/briar/plugins/modem/ModemImpl.java
Normal file
304
src/net/sf/briar/plugins/modem/ModemImpl.java
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
package net.sf.briar.plugins.modem;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static jssc.SerialPort.PURGE_RXCLEAR;
|
||||||
|
import static jssc.SerialPort.PURGE_TXCLEAR;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import jssc.SerialPort;
|
||||||
|
import jssc.SerialPortEvent;
|
||||||
|
import jssc.SerialPortEventListener;
|
||||||
|
import jssc.SerialPortException;
|
||||||
|
|
||||||
|
class ModemImpl implements Modem, SerialPortEventListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(ModemImpl.class.getName());
|
||||||
|
private static final int MAX_LINE_LENGTH = 256;
|
||||||
|
private static final int[] BAUD_RATES = {
|
||||||
|
256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200
|
||||||
|
};
|
||||||
|
private static final int OK_TIMEOUT = 5 * 1000; // Milliseconds
|
||||||
|
private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds
|
||||||
|
|
||||||
|
private final Callback callback;
|
||||||
|
private final SerialPort port;
|
||||||
|
private final AtomicBoolean initialised, offHook, connected;
|
||||||
|
private final byte[] line;
|
||||||
|
|
||||||
|
private int lineLen = 0;
|
||||||
|
|
||||||
|
// A fresh queue is used for each connection
|
||||||
|
private volatile BlockingQueue<byte[]> received;
|
||||||
|
|
||||||
|
ModemImpl(Callback callback, String portName) {
|
||||||
|
this.callback = callback;
|
||||||
|
port = new SerialPort(portName);
|
||||||
|
initialised = new AtomicBoolean(false);
|
||||||
|
offHook = new AtomicBoolean(false);
|
||||||
|
connected = new AtomicBoolean(false);
|
||||||
|
line = new byte[MAX_LINE_LENGTH];
|
||||||
|
received = new LinkedBlockingQueue<byte[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() throws IOException {
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Initialising");
|
||||||
|
try {
|
||||||
|
// Open the serial port
|
||||||
|
if(!port.openPort())
|
||||||
|
throw new IOException("Failed to open serial port");
|
||||||
|
// Find a suitable baud rate
|
||||||
|
boolean foundBaudRate = false;
|
||||||
|
for(int baudRate : BAUD_RATES) {
|
||||||
|
if(port.setParams(baudRate, 8, 1, 0)) {
|
||||||
|
foundBaudRate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!foundBaudRate)
|
||||||
|
throw new IOException("Could not find a suitable baud rate");
|
||||||
|
// Listen for incoming data and hangup events
|
||||||
|
port.addEventListener(this);
|
||||||
|
// Initialise the modem
|
||||||
|
port.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR);
|
||||||
|
port.writeBytes("ATZ\r\n".getBytes("US-ASCII")); // Reset
|
||||||
|
port.writeBytes("ATE0\r\n".getBytes("US-ASCII")); // Echo off
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Wait for the modem to respond "OK"
|
||||||
|
synchronized(initialised) {
|
||||||
|
if(!initialised.get()) initialised.wait(OK_TIMEOUT);
|
||||||
|
if(!initialised.get())
|
||||||
|
throw new IOException("Modem did not respond");
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Interrupted while initialising modem");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException("Interrupted while initialising modem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean dial(String number) throws IOException {
|
||||||
|
if(offHook.getAndSet(true)) {
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Not dialling - call in progress");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Dialling");
|
||||||
|
try {
|
||||||
|
port.writeBytes(("ATDT" + number + "\r\n").getBytes("US-ASCII"));
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
synchronized(connected) {
|
||||||
|
if(!connected.get()) connected.wait(CONNECT_TIMEOUT);
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Interrupted while connecting outgoing call");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException("Interrupted while connecting outgoing call");
|
||||||
|
}
|
||||||
|
if(connected.get()) return true;
|
||||||
|
hangUp();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return new ModemInputStream(received);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return new ModemOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hangUp() throws IOException {
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Hanging up");
|
||||||
|
try {
|
||||||
|
port.setDTR(false);
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
received.add(new byte[0]); // Empty buffer indicates EOF
|
||||||
|
received = new LinkedBlockingQueue<byte[]>();
|
||||||
|
connected.set(false);
|
||||||
|
offHook.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serialEvent(SerialPortEvent ev) {
|
||||||
|
try {
|
||||||
|
if(ev.isRXCHAR()) {
|
||||||
|
byte[] b = port.readBytes();
|
||||||
|
if(connected.get()) received.add(b);
|
||||||
|
else handleText(b);
|
||||||
|
} else if(ev.isDSR() && ev.getEventValue() == 0) {
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up");
|
||||||
|
hangUp();
|
||||||
|
}
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleText(byte[] b) throws IOException {
|
||||||
|
if(lineLen + b.length > MAX_LINE_LENGTH)
|
||||||
|
throw new IOException("Line too long");
|
||||||
|
for(int i = 0; i < b.length; i++) {
|
||||||
|
line[lineLen] = b[i];
|
||||||
|
if(b[i] == '\n') {
|
||||||
|
String s = new String(line, 0, lineLen, "US-ASCII").trim();
|
||||||
|
lineLen = 0;
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
|
||||||
|
if(s.startsWith("CONNECT")) {
|
||||||
|
// There might be data in the buffer as well as text
|
||||||
|
int off = i + 1;
|
||||||
|
if(off < b.length) {
|
||||||
|
byte[] data = new byte[b.length - off];
|
||||||
|
System.arraycopy(b, off, data, 0, data.length);
|
||||||
|
received.add(data);
|
||||||
|
}
|
||||||
|
synchronized(connected) {
|
||||||
|
if(!connected.getAndSet(true))
|
||||||
|
connected.notifyAll();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if(s.equals("OK")) {
|
||||||
|
synchronized(initialised) {
|
||||||
|
if(!initialised.getAndSet(true))
|
||||||
|
initialised.notifyAll();
|
||||||
|
}
|
||||||
|
} else if(s.equals("RING")) {
|
||||||
|
// FIXME: Don't do this on the event thread
|
||||||
|
answer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lineLen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void answer() throws IOException {
|
||||||
|
if(offHook.getAndSet(true)) {
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Not answering - call in progress");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Answering");
|
||||||
|
try {
|
||||||
|
port.writeBytes("ATA\r\n".getBytes("US-ASCII"));
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
synchronized(connected) {
|
||||||
|
if(!connected.get()) connected.wait(CONNECT_TIMEOUT);
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Interrupted while connecting incoming call");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException("Interrupted while connecting incoming call");
|
||||||
|
}
|
||||||
|
if(connected.get()) callback.incomingCallConnected();
|
||||||
|
else hangUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModemInputStream extends InputStream {
|
||||||
|
|
||||||
|
private final BlockingQueue<byte[]> received;
|
||||||
|
|
||||||
|
private byte[] buf = null;
|
||||||
|
private int offset = 0;
|
||||||
|
|
||||||
|
private ModemInputStream(BlockingQueue<byte[]> received) {
|
||||||
|
this.received = received;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
getBufferIfNecessary();
|
||||||
|
if(buf.length == 0) return -1;
|
||||||
|
return buf[offset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
getBufferIfNecessary();
|
||||||
|
if(buf.length == 0) return -1;
|
||||||
|
int len = Math.min(b.length, buf.length - offset);
|
||||||
|
System.arraycopy(buf, offset, b, 0, len);
|
||||||
|
offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
getBufferIfNecessary();
|
||||||
|
if(buf.length == 0) return -1;
|
||||||
|
len = Math.min(len, buf.length - offset);
|
||||||
|
System.arraycopy(buf, offset, b, off, len);
|
||||||
|
offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getBufferIfNecessary() throws IOException {
|
||||||
|
if(buf == null || offset == buf.length) {
|
||||||
|
try {
|
||||||
|
buf = received.take();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Interrupted while reading");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModemOutputStream extends OutputStream {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
try {
|
||||||
|
port.writeByte((byte) b);
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b) throws IOException {
|
||||||
|
try {
|
||||||
|
port.writeBytes(b);
|
||||||
|
} catch(SerialPortException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
if(len < b.length) {
|
||||||
|
byte[] copy = new byte[len];
|
||||||
|
System.arraycopy(b, off, copy, 0, len);
|
||||||
|
write(copy);
|
||||||
|
} else {
|
||||||
|
write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user