Changed the root package from net.sf.briar to org.briarproject.

This commit is contained in:
akwizgran
2014-01-08 16:18:30 +00:00
parent dce70f487c
commit 832476412c
427 changed files with 2507 additions and 2507 deletions

View File

@@ -0,0 +1,286 @@
package org.briarproject.plugins.modem;
import java.util.HashMap;
import java.util.Map;
class CountryCodes {
private static final Country[] COUNTRIES = {
new Country("AD", "Andorra", "376", "00", ""),
new Country("AE", "United Arab Emirates", "971", "00", "0"),
new Country("AF", "Afghanistan", "93", "00", "0"),
new Country("AG", "Antigua and Barbuda", "1", "011", "1"),
new Country("AI", "Anguilla", "1", "011", "1"),
new Country("AL", "Albania", "355", "00", "0"),
new Country("AM", "Armenia", "374", "00", "8"),
new Country("AN", "Netherlands Antilles", "599", "00", "0"),
new Country("AO", "Angola", "244", "00", "0"),
new Country("AQ", "Antarctica", "672", "", ""),
new Country("AR", "Argentina", "54", "00", "0"),
new Country("AS", "American Samoa", "1", "011", "1"),
new Country("AT", "Austria", "43", "00", "0"),
new Country("AU", "Australia", "61", "0011", "0"),
new Country("AW", "Aruba", "297", "00", ""),
new Country("AZ", "Azerbaijan", "994", "00", "8"),
new Country("BA", "Bosnia and Herzegovina", "387", "00", "0"),
new Country("BB", "Barbados", "1", "011", "1"),
new Country("BD", "Bangladesh", "880", "00", "0"),
new Country("BE", "Belgium", "32", "00", "0"),
new Country("BF", "Burkina Faso", "226", "00", ""),
new Country("BG", "Bulgaria", "359", "00", "0"),
new Country("BH", "Bahrain", "973", "00", ""),
new Country("BI", "Burundi", "257", "00", ""),
new Country("BJ", "Benin", "229", "00", ""),
new Country("BM", "Bermuda", "1", "011", "1"),
new Country("BN", "Brunei Darussalam", "673", "00", "0"),
new Country("BO", "Bolivia", "591", "00", "0"),
new Country("BR", "Brazil", "55", "00", "0"),
new Country("BS", "Bahamas", "1", "011", "1"),
new Country("BT", "Bhutan", "975", "00", ""),
new Country("BV", "Bouvet Island (Norway)", "47", "00", ""),
new Country("BW", "Botswana", "267", "00", ""),
new Country("BY", "Belarus", "375", "8**10", "8"),
new Country("BZ", "Belize", "501", "00", "0"),
new Country("CA", "Canada", "1", "011", "1"),
new Country("CC", "Cocos (Keeling) Islands", "61", "0011", "0"),
new Country("CD", "Congo (Republic)", "243", "00", ""),
new Country("CF", "Central African Republic", "236", "00", ""),
new Country("CG", "Congo (Democratic Republic)", "242", "00", "0"),
new Country("CH", "Switzerland", "41", "00", "0"),
new Country("CI", "Cote D'Ivoire", "225", "00", "0"),
new Country("CK", "Cook Islands", "682", "00", "00"),
new Country("CL", "Chile", "56", "00", "0"),
new Country("CM", "Cameroon", "237", "00", ""),
new Country("CN", "China", "86", "00", "0"),
new Country("CO", "Colombia", "57", "009", "09"),
new Country("CR", "Costa Rica", "506", "00", ""),
new Country("CU", "Cuba", "53", "119", "0"),
new Country("CV", "Cape Verde Islands", "238", "0", ""),
new Country("CX", "Christmas Island", "61", "0011", "0"),
new Country("CY", "Cyprus", "357", "00", ""),
new Country("CZ", "Czech Republic", "420", "00", ""),
new Country("DE", "Germany", "49", "00", "0"),
new Country("DJ", "Djibouti", "253", "00", ""),
new Country("DK", "Denmark", "45", "00", ""),
new Country("DM", "Dominica", "1", "011", "1"),
new Country("DO", "Dominican Republic", "1", "011", "1"),
new Country("DZ", "Algeria", "213", "00", "7"),
new Country("EC", "Ecuador", "593", "00", "0"),
new Country("EE", "Estonia", "372", "00", ""),
new Country("EG", "Egypt", "20", "00", "0"),
new Country("EH", "Western Sahara", "212", "00", "0"),
new Country("ER", "Eritrea", "291", "00", "0"),
new Country("ES", "Spain", "34", "00", ""),
new Country("ET", "Ethiopia", "251", "00", "0"),
new Country("FI", "Finland", "358", "00", "0"),
new Country("FJ", "Fiji", "679", "00", ""),
new Country("FK", "Falkland Islands (Malvinas)", "500", "00", ""),
new Country("FM", "Micronesia", "691", "011", "1"),
new Country("FO", "Faroe Islands", "298", "00", ""),
new Country("FR", "France", "33", "00", ""),
new Country("GA", "Gabonese Republic", "241", "00", ""),
new Country("GB", "United Kingdom", "44", "00", "0"),
new Country("GD", "Grenada", "1", "011", "4"),
new Country("GE", "Georgia", "995", "8**10", "8"),
new Country("GF", "French Guiana", "594", "00", ""),
new Country("GH", "Ghana", "233", "00", ""),
new Country("GI", "Gibraltar", "350", "00", ""),
new Country("GL", "Greenland", "299", "00", ""),
new Country("GM", "Gambia", "220", "00", ""),
new Country("GN", "Guinea", "224", "00", "0"),
new Country("GP", "Guadeloupe", "590", "00", ""),
new Country("GQ", "Equatorial Guinea", "240", "00", ""),
new Country("GR", "Greece", "30", "00", ""),
new Country("GS", "South Georgia and the South Sandwich Islands", "995", "8**10", "8"),
new Country("GT", "Guatemala", "502", "00", ""),
new Country("GU", "Guam", "1", "011", "1"),
new Country("GW", "Guinea-Bissau", "245", "00", ""),
new Country("GY", "Guyana", "592", "001", "0"),
new Country("HK", "Hong Kong", "852", "001", ""),
new Country("HM", "Heard Island and McDonald Islands", "692", "00", "0"),
new Country("HN", "Honduras", "504", "00", "0"),
new Country("HR", "Croatia", "385", "00", "0"),
new Country("HT", "Haiti", "509", "00", "0"),
new Country("HU", "Hungary", "36", "00", "06"),
new Country("ID", "Indonesia", "62", "001", "0"),
new Country("IE", "Ireland", "353", "00", "0"),
new Country("IL", "Israel", "972", "00", "0"),
new Country("IN", "India", "91", "00", "0"),
new Country("IO", "British Indian Ocean Territory", "246", "00", ""),
new Country("IQ", "Iraq", "964", "00", "0"),
new Country("IR", "Iran", "98", "00", "0"),
new Country("IS", "Iceland", "354", "00", "0"),
new Country("IT", "Italy", "39", "00", ""),
new Country("JM", "Jamaica", "1", "011", "1"),
new Country("JO", "Jordan", "962", "00", "0"),
new Country("JP", "Japan", "81", "001", "0"),
new Country("KE", "Kenya", "254", "000", "0"),
new Country("KG", "Kyrgyzstan", "996", "00", "0"),
new Country("KH", "Cambodia", "855", "001", "0"),
new Country("KI", "Kiribati", "686", "00", "0"),
new Country("KM", "Comoros", "269", "00", ""),
new Country("KN", "Saint Kitts and Nevis", "1", "011", "1"),
new Country("KP", "Korea (North)", "850", "00", "0"),
new Country("KR", "Korea (South)", "82", "001", "0"),
new Country("KW", "Kuwait", "965", "00", "0"),
new Country("KY", "Cayman Islands", "1", "011", "1"),
new Country("KZ", "Kazakhstan", "7", "8**10", "8"),
new Country("LA", "Laos", "856", "00", "0"),
new Country("LB", "Lebanon", "961", "00", "0"),
new Country("LC", "Saint Lucia", "1", "011", "1"),
new Country("LI", "Liechtenstein", "423", "00", ""),
new Country("LK", "Sri Lanka", "94", "00", "0"),
new Country("LR", "Liberia", "231", "00", "22"),
new Country("LS", "Lesotho", "266", "00", "0"),
new Country("LT", "Lithuania", "370", "00", "8"),
new Country("LU", "Luxembourg", "352", "00", ""),
new Country("LV", "Latvia", "371", "00", "8"),
new Country("LY", "Libya", "218", "00", "0"),
new Country("MA", "Morocco", "212", "00", ""),
new Country("MC", "Monaco", "377", "00", "0"),
new Country("MD", "Moldova", "373", "00", "0"),
new Country("ME", "Montenegro", "382", "99", "0"),
new Country("MG", "Madagascar", "261", "00", "0"),
new Country("MH", "Marshall Islands", "692", "011", "1"),
new Country("MK", "Macedonia", "389", "00", "0"),
new Country("ML", "Mali", "223", "00", "0"),
new Country("MM", "Myanmar", "95", "00", ""),
new Country("MN", "Mongolia", "976", "001", "0"),
new Country("MO", "Macao", "853", "00", "0"),
new Country("MP", "Northern Mariana Islands", "1", "011", "1"),
new Country("MQ", "Martinique", "596", "00", "0"),
new Country("MR", "Mauritania", "222", "00", "0"),
new Country("MS", "Montserrat", "1", "011", "1"),
new Country("MT", "Malta", "356", "00", "21"),
new Country("MU", "Mauritius", "230", "00", "0"),
new Country("MV", "Maldives", "960", "00", "0"),
new Country("MW", "Malawi", "265", "00", ""),
new Country("MX", "Mexico", "52", "00", "01"),
new Country("MY", "Malaysia", "60", "00", "0"),
new Country("MZ", "Mozambique", "258", "00", "0"),
new Country("NA", "Namibia", "264", "00", "0"),
new Country("NC", "New Caledonia", "687", "00", "0"),
new Country("NE", "Niger", "227", "00", "0"),
new Country("NF", "Norfolk Island", "672", "00", ""),
new Country("NG", "Nigeria", "234", "009", "0"),
new Country("NI", "Nicaragua", "505", "00", "0"),
new Country("NL", "Netherlands", "31", "00", "0"),
new Country("NO", "Norway", "47", "00", ""),
new Country("NP", "Nepal", "977", "00", "0"),
new Country("NR", "Nauru", "674", "00", "0"),
new Country("NU", "Niue", "683", "00", "0"),
new Country("NZ", "New Zealand", "64", "00", "0"),
new Country("OM", "Oman", "968", "00", "0"),
new Country("PA", "Panama", "507", "00", "0"),
new Country("PE", "Peru", "51", "00", "0"),
new Country("PF", "French Polynesia", "689", "00", ""),
new Country("PG", "Papua New Guinea", "675", "05", ""),
new Country("PH", "Philippines", "63", "00", "0"),
new Country("PK", "Pakistan", "92", "00", "0"),
new Country("PL", "Poland", "48", "00", "0"),
new Country("PM", "Saint Pierre and Miquelon", "508", "00", "0"),
new Country("PN", "Pitcairn", "872", "", ""),
new Country("PR", "Puerto Rico", "1", "011", "1"),
new Country("PS", "Palestine", "970", "00", "0"),
new Country("PT", "Portugal", "351", "00", ""),
new Country("PW", "Palau", "680", "011", ""),
new Country("PY", "Paraguay", "595", "002", "0"),
new Country("QA", "Qatar", "974", "00", "0"),
new Country("RE", "Reunion", "262", "00", "0"),
new Country("RO", "Romania", "40", "00", "0"),
new Country("RS", "Serbia", "381", "99", "0"),
new Country("RU", "Russia", "7", "8**10", "8"),
new Country("RW", "Rwanda", "250", "00", "0"),
new Country("SA", "Saudi Arabia", "966", "00", "0"),
new Country("SB", "Solomon Islands", "677", "00", ""),
new Country("SC", "Seychelles", "248", "00", "0"),
new Country("SD", "Sudan", "249", "00", "0"),
new Country("SE", "Sweden", "46", "00", "0"),
new Country("SG", "Singapore", "65", "001", ""),
new Country("SH", "Saint Helena", "290", "00", ""),
new Country("SI", "Slovenia", "386", "00", "0"),
new Country("SJ", "Svalbard and Jan Mayen", "378", "00", "0"),
new Country("SK", "Slovakia", "421", "00", "0"),
new Country("SL", "Sierra Leone", "232", "00", "0"),
new Country("SM", "San Marino", "378", "00", "0"),
new Country("SN", "Senegal", "221", "00", "0"),
new Country("SO", "Somalia", "252", "00", ""),
new Country("SR", "Suriname", "597", "00", ""),
new Country("ST", "Sao Tome and Principe", "239", "00", "0"),
new Country("SV", "El Salvador", "503", "00", ""),
new Country("SY", "Syria", "963", "00", "0"),
new Country("SZ", "Swaziland", "268", "00", ""),
new Country("TC", "Turks and Caicos Islands", "1", "011", "1"),
new Country("TD", "Chad", "235", "15", ""),
new Country("TF", "French Southern Territories", "596", "00", "0"),
new Country("TG", "Togo", "228", "00", ""),
new Country("TH", "Thailand", "66", "001", "0"),
new Country("TJ", "Tajikistan", "992", "8**10", "8"),
new Country("TK", "Tokelau", "690", "00", ""),
new Country("TL", "Timor-Leste", "670", "00", ""),
new Country("TM", "Turkmenistan", "993", "8**10", "8"),
new Country("TN", "Tunisia", "216", "00", "0"),
new Country("TO", "Tonga Islands", "676", "00", ""),
new Country("TR", "Turkey", "90", "00", "0"),
new Country("TT", "Trinidad and Tobago", "1", "011", "1"),
new Country("TV", "Tuvalu", "688", "00", ""),
new Country("TW", "Taiwan", "886", "002", ""),
new Country("TZ", "Tanzania", "255", "000", "0"),
new Country("UA", "Ukraine", "380", "8**10", "8"),
new Country("UG", "Uganda", "256", "000", "0"),
new Country("US", "United States", "1", "011", "1"),
new Country("UY", "Uruguay", "598", "00", "0"),
new Country("UZ", "Uzbekistan", "998", "8**10", "8"),
new Country("VA", "Holy See (Vatican City State)", "379", "00", ""),
new Country("VC", "Saint Vincent and the Grenadines", "1", "011", "1"),
new Country("VE", "Venezuela", "58", "00", "0"),
new Country("VG", "Virgin Islands (British)", "1", "011", "1"),
new Country("VI", "Virgin Islands (U.S.)", "1", "011", "1"),
new Country("VN", "Viet Nam", "84", "00", "0"),
new Country("VU", "Vanuatu", "678", "00", ""),
new Country("WF", "Wallis and Futuna Islands", "681", "19", ""),
new Country("WS", "Samoa (Western)", "685", "0", "0"),
new Country("YE", "Yemen", "967", "00", "0"),
new Country("YT", "Mayotte", "269", "00", ""),
new Country("ZA", "South Africa", "27", "09", "0"),
new Country("ZM", "Zambia", "260", "00", "0"),
new Country("ZW", "Zimbabwe", "263", "110", "0")
};
private static final Map<String, Country> COUNTRY_MAP =
new HashMap<String, Country>();
static {
for(Country c : COUNTRIES) COUNTRY_MAP.put(c.iso3166, c);
}
static String translate(String number, String callerIso, String calleeIso) {
Country from = COUNTRY_MAP.get(callerIso);
Country to = COUNTRY_MAP.get(calleeIso);
if(from == null || to == null) return null;
// Strip any prefixes and country codes from the number
String plusCountryCode = "+" + to.countryCode;
String iddCountryCode = to.idd + to.countryCode;
if(number.startsWith(plusCountryCode))
number = number.substring(plusCountryCode.length());
else if(number.startsWith(iddCountryCode))
number = number.substring(iddCountryCode.length());
else if(number.startsWith(to.ndd))
number = number.substring(to.ndd.length());
if(from == to) return from.ndd + number; // National
return from.idd + to.countryCode + number; // International
}
private static class Country {
private final String iso3166, countryCode, idd, ndd;
private Country(String iso3166, String englishName, String countryCode,
String idd, String ndd) {
this.iso3166 = iso3166;
this.countryCode = countryCode;
this.idd = idd;
this.ndd = ndd;
}
}
}

View File

@@ -0,0 +1,45 @@
package org.briarproject.plugins.modem;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A modem that can be used for multiple sequential incoming and outgoing
* calls. If an exception is thrown, a new modem instance must be created.
*/
interface Modem {
/**
* Call this method after creating the modem and before making any calls.
* If this method returns false the modem cannot be used.
*/
boolean start() throws IOException;
/**
* Call this method when the modem is no longer needed. If a call is in
* progress it will be terminated.
*/
void stop() 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() throws IOException;
/** Returns a stream for writing to the currently connected call. */
OutputStream getOutputStream() throws IOException;
/** Hangs up the modem, ending the currently connected call. */
void hangUp() throws IOException;
interface Callback {
/** Called when an incoming call connects. */
void incomingCallConnected();
}
}

View File

@@ -0,0 +1,6 @@
package org.briarproject.plugins.modem;
interface ModemFactory {
Modem createModem(Modem.Callback callback, String portName);
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.plugins.modem;
import java.util.concurrent.Executor;
import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.SystemClock;
class ModemFactoryImpl implements ModemFactory {
private final Executor executor;
private final ReliabilityLayerFactory reliabilityFactory;
private final Clock clock;
ModemFactoryImpl(Executor executor,
ReliabilityLayerFactory reliabilityFactory) {
this.executor = executor;
this.reliabilityFactory = reliabilityFactory;
clock = new SystemClock();
}
public Modem createModem(Modem.Callback callback, String portName) {
return new ModemImpl(executor, reliabilityFactory, clock, callback,
new SerialPortImpl(portName));
}
}

View File

@@ -0,0 +1,408 @@
package org.briarproject.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.Executor;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import org.briarproject.api.reliability.ReliabilityLayer;
import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.api.reliability.WriteHandler;
import org.briarproject.api.system.Clock;
class ModemImpl implements Modem, WriteHandler, 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 = 2 * 60 * 1000; // Milliseconds
private static final int ESCAPE_SEQUENCE_GUARD_TIME = 1000; // Milliseconds
private final Executor executor;
private final ReliabilityLayerFactory reliabilityFactory;
private final Clock clock;
private final Callback callback;
private final SerialPort port;
private final Semaphore stateChange;
private final byte[] line;
private int lineLen = 0;
private ReliabilityLayer reliability = null; // Locking: this
private boolean initialised = false, connected = false; // Locking: this
ModemImpl(Executor executor, ReliabilityLayerFactory reliabilityFactory,
Clock clock, Callback callback, SerialPort port) {
this.executor = executor;
this.reliabilityFactory = reliabilityFactory;
this.clock = clock;
this.callback = callback;
this.port = port;
stateChange = new Semaphore(1);
line = new byte[MAX_LINE_LENGTH];
}
public boolean start() throws IOException {
if(LOG.isLoggable(INFO)) LOG.info("Starting");
try {
stateChange.acquire();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted while waiting to start");
}
try {
// Open the serial port
port.openPort();
// Find a suitable baud rate and initialise the modem
try {
boolean foundBaudRate = false;
for(int baudRate : BAUD_RATES) {
if(port.setParams(baudRate, 8, 1, 0)) {
foundBaudRate = true;
break;
}
}
if(!foundBaudRate) {
tryToClose(port);
throw new IOException("No suitable baud rate");
}
port.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR);
port.addEventListener(this);
port.writeBytes("ATZ\r\n".getBytes("US-ASCII")); // Reset
port.writeBytes("ATE0\r\n".getBytes("US-ASCII")); // Echo off
} catch(IOException e) {
tryToClose(port);
throw e;
}
// Wait for the event thread to receive "OK"
boolean success = false;
try {
synchronized(this) {
long now = clock.currentTimeMillis();
long end = now + OK_TIMEOUT;
while(now < end && !initialised) {
wait(end - now);
now = clock.currentTimeMillis();
}
success = initialised;
}
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while initialising");
}
if(success) return true;
tryToClose(port);
return false;
} finally {
stateChange.release();
}
}
private void tryToClose(SerialPort port) {
try {
port.closePort();
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
public void stop() throws IOException {
if(LOG.isLoggable(INFO)) LOG.info("Stopping");
// Wake any threads that are waiting to connect
synchronized(this) {
initialised = false;
connected = false;
notifyAll();
}
// Hang up if necessary and close the port
try {
stateChange.acquire();
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while waiting to stop");
}
try {
hangUpInner();
port.closePort();
} finally {
stateChange.release();
}
}
// Locking: stateChange
private void hangUpInner() throws IOException {
ReliabilityLayer reliability;
synchronized(this) {
if(this.reliability == null) {
if(LOG.isLoggable(INFO))
LOG.info("Not hanging up - already on the hook");
return;
}
reliability = this.reliability;
this.reliability = null;
connected = false;
}
reliability.stop();
if(LOG.isLoggable(INFO)) LOG.info("Hanging up");
try {
clock.sleep(ESCAPE_SEQUENCE_GUARD_TIME);
port.writeBytes("+++".getBytes("US-ASCII"));
clock.sleep(ESCAPE_SEQUENCE_GUARD_TIME);
port.writeBytes("ATH\r\n".getBytes("US-ASCII"));
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while hanging up");
} catch(IOException e) {
tryToClose(port);
throw e;
}
}
public boolean dial(String number) throws IOException {
if(!stateChange.tryAcquire()) {
if(LOG.isLoggable(INFO))
LOG.info("Not dialling - state change in progress");
return false;
}
try {
ReliabilityLayer reliability =
reliabilityFactory.createReliabilityLayer(this);
synchronized(this) {
if(!initialised) {
if(LOG.isLoggable(INFO))
LOG.info("Not dialling - modem not initialised");
return false;
}
if(this.reliability != null) {
if(LOG.isLoggable(INFO))
LOG.info("Not dialling - call in progress");
return false;
}
this.reliability = reliability;
}
reliability.start();
if(LOG.isLoggable(INFO)) LOG.info("Dialling");
try {
String dial = "ATDT" + number + "\r\n";
port.writeBytes(dial.getBytes("US-ASCII"));
} catch(IOException e) {
tryToClose(port);
throw e;
}
// Wait for the event thread to receive "CONNECT"
try {
synchronized(this) {
long now = clock.currentTimeMillis();
long end = now + CONNECT_TIMEOUT;
while(now < end && initialised && !connected) {
wait(end - now);
now = clock.currentTimeMillis();
}
if(connected) return true;
}
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while dialling");
}
hangUpInner();
return false;
} finally {
stateChange.release();
}
}
public InputStream getInputStream() throws IOException {
ReliabilityLayer reliability;
synchronized(this) {
reliability = this.reliability;
}
if(reliability == null) throw new IOException("Not connected");
return reliability.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
ReliabilityLayer reliability;
synchronized(this) {
reliability = this.reliability;
}
if(reliability == null) throw new IOException("Not connected");
return reliability.getOutputStream();
}
public void hangUp() throws IOException {
try {
stateChange.acquire();
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while waiting to hang up");
}
try {
hangUpInner();
} finally {
stateChange.release();
}
}
public void handleWrite(byte[] b) throws IOException {
try {
port.writeBytes(b);
} catch(IOException e) {
tryToClose(port);
throw e;
}
}
public void serialEvent(SerialPortEvent ev) {
try {
if(ev.isRXCHAR()) {
byte[] b = port.readBytes();
if(!handleData(b)) handleText(b);
} else if(ev.isDSR() && ev.getEventValue() == 0) {
if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up");
hangUp();
} else {
if(LOG.isLoggable(INFO)) {
LOG.info("Serial event " + ev.getEventType() + " " +
ev.getEventValue());
}
}
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private boolean handleData(byte[] b) throws IOException {
ReliabilityLayer reliability;
synchronized(this) {
reliability = this.reliability;
}
if(reliability == null) return false;
reliability.handleRead(b);
return true;
}
private void handleText(byte[] b) throws IOException {
if(lineLen + b.length > MAX_LINE_LENGTH) {
tryToClose(port);
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")) {
synchronized(this) {
connected = true;
notifyAll();
}
// 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);
handleData(data);
}
return;
} else if(s.equals("BUSY") || s.equals("NO DIALTONE")
|| s.equals("NO CARRIER")) {
synchronized(this) {
connected = false;
notifyAll();
}
} else if(s.equals("OK")) {
synchronized(this) {
initialised = true;
notifyAll();
}
} else if(s.equals("RING")) {
executor.execute(new Runnable() {
public void run() {
try {
answer();
} catch(IOException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
} else {
lineLen++;
}
}
}
private void answer() throws IOException {
if(!stateChange.tryAcquire()) {
if(LOG.isLoggable(INFO))
LOG.info("Not answering - state change in progress");
return;
}
try {
ReliabilityLayer reliability =
reliabilityFactory.createReliabilityLayer(this);
synchronized(this) {
if(!initialised) {
if(LOG.isLoggable(INFO))
LOG.info("Not answering - modem not initialised");
return;
}
if(this.reliability != null) {
if(LOG.isLoggable(INFO))
LOG.info("Not answering - call in progress");
return;
}
this.reliability = reliability;
}
reliability.start();
if(LOG.isLoggable(INFO)) LOG.info("Answering");
try {
port.writeBytes("ATA\r\n".getBytes("US-ASCII"));
} catch(IOException e) {
tryToClose(port);
throw e;
}
// Wait for the event thread to receive "CONNECT"
boolean success = false;
try {
synchronized(this) {
long now = clock.currentTimeMillis();
long end = now + CONNECT_TIMEOUT;
while(now < end && initialised && !connected) {
wait(end - now);
now = clock.currentTimeMillis();
}
success = connected;
}
} catch(InterruptedException e) {
tryToClose(port);
Thread.currentThread().interrupt();
throw new IOException("Interrupted while answering");
}
if(success) callback.incomingCallConnected();
else hangUpInner();
} finally {
stateChange.release();
}
}
}

View File

@@ -0,0 +1,273 @@
package org.briarproject.plugins.modem;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
import org.briarproject.api.TransportProperties;
import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.util.StringUtils;
class ModemPlugin implements DuplexPlugin, Modem.Callback {
static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("8f573867bedf54884b5868ee5d902832" +
"ee5e522da84d0d431712bd672fbd2f79" +
"262d27b93879b94ee9afbb80e7fc87fb");
static final TransportId ID = new TransportId(TRANSPORT_ID);
private static final Logger LOG =
Logger.getLogger(ModemPlugin.class.getName());
private final Executor pluginExecutor;
private final ModemFactory modemFactory;
private final SerialPortList serialPortList;
private final DuplexPluginCallback callback;
private final int maxFrameLength;
private final long maxLatency, pollingInterval;
private final boolean shuffle; // Used to disable shuffling for testing
private volatile boolean running = false;
private volatile Modem modem = null;
ModemPlugin(Executor pluginExecutor, ModemFactory modemFactory,
SerialPortList serialPortList, DuplexPluginCallback callback,
int maxFrameLength, long maxLatency, long pollingInterval,
boolean shuffle) {
this.pluginExecutor = pluginExecutor;
this.modemFactory = modemFactory;
this.serialPortList = serialPortList;
this.callback = callback;
this.maxFrameLength = maxFrameLength;
this.maxLatency = maxLatency;
this.pollingInterval = pollingInterval;
this.shuffle = shuffle;
}
public TransportId getId() {
return ID;
}
public String getName() {
return "MODEM_PLUGIN_NAME";
}
public int getMaxFrameLength() {
return maxFrameLength;
}
public long getMaxLatency() {
return maxLatency;
}
public boolean start() {
for(String portName : serialPortList.getPortNames()) {
if(LOG.isLoggable(INFO))
LOG.info("Trying to initialise modem on " + portName);
modem = modemFactory.createModem(this, portName);
try {
if(!modem.start()) continue;
if(LOG.isLoggable(INFO))
LOG.info("Initialised modem on " + portName);
running = true;
return true;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
return false;
}
public void stop() {
running = false;
if(modem != null) {
try {
modem.stop();
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}
private boolean resetModem() {
if(!running) return false;
for(String portName : serialPortList.getPortNames()) {
if(LOG.isLoggable(INFO))
LOG.info("Trying to initialise modem on " + portName);
modem = modemFactory.createModem(this, portName);
try {
if(!modem.start()) continue;
if(LOG.isLoggable(INFO))
LOG.info("Initialised modem on " + portName);
return true;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
running = false;
return false;
}
public boolean shouldPoll() {
return true;
}
public long getPollingInterval() {
return pollingInterval;
}
public void poll(Collection<ContactId> connected) {
if(!connected.isEmpty()) return; // One at a time please
pluginExecutor.execute(new Runnable() {
public void run() {
poll();
}
});
}
private void poll() {
if(!running) return;
// Get the ISO 3166 code for the caller's country
String callerIso = callback.getLocalProperties().get("iso3166");
if(StringUtils.isNullOrEmpty(callerIso)) return;
// Call contacts one at a time in a random order
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
List<ContactId> contacts = new ArrayList<ContactId>(remote.keySet());
if(shuffle) Collections.shuffle(contacts);
Iterator<ContactId> it = contacts.iterator();
while(it.hasNext() && running) {
ContactId c = it.next();
// Get the ISO 3166 code for the callee's country
TransportProperties properties = remote.get(c);
if(properties == null) continue;
String calleeIso = properties.get("iso3166");
if(StringUtils.isNullOrEmpty(calleeIso)) continue;
// Get the callee's phone number
String number = properties.get("number");
if(StringUtils.isNullOrEmpty(number)) continue;
// Convert the number into direct dialling form
number = CountryCodes.translate(number, callerIso, calleeIso);
if(number == null) continue;
// Dial the number
try {
if(!modem.dial(number)) continue;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if(resetModem()) continue;
break;
}
if(LOG.isLoggable(INFO)) LOG.info("Outgoing call connected");
ModemTransportConnection conn = new ModemTransportConnection();
callback.outgoingConnectionCreated(c, conn);
try {
conn.waitForDisposal();
} catch(InterruptedException e) {
if(LOG.isLoggable(WARNING))
LOG.warning("Interrupted while polling");
Thread.currentThread().interrupt();
break;
}
}
}
public DuplexTransportConnection createConnection(ContactId c) {
if(!running) return null;
// Get the ISO 3166 code for the caller's country
String fromIso = callback.getLocalProperties().get("iso3166");
if(StringUtils.isNullOrEmpty(fromIso)) return null;
// Get the ISO 3166 code for the callee's country
TransportProperties properties = callback.getRemoteProperties().get(c);
if(properties == null) return null;
String toIso = properties.get("iso3166");
if(StringUtils.isNullOrEmpty(toIso)) return null;
// Get the callee's phone number
String number = properties.get("number");
if(StringUtils.isNullOrEmpty(number)) return null;
// Convert the number into direct dialling form
number = CountryCodes.translate(number, fromIso, toIso);
if(number == null) return null;
// Dial the number
try {
if(!modem.dial(number)) return null;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
resetModem();
return null;
}
return new ModemTransportConnection();
}
public boolean supportsInvitations() {
return false;
}
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout) {
throw new UnsupportedOperationException();
}
public void incomingCallConnected() {
if(LOG.isLoggable(INFO)) LOG.info("Incoming call connected");
callback.incomingConnectionCreated(new ModemTransportConnection());
}
private class ModemTransportConnection
implements DuplexTransportConnection {
private final CountDownLatch finished = new CountDownLatch(1);
public int getMaxFrameLength() {
return maxFrameLength;
}
public long getMaxLatency() {
return maxLatency;
}
public InputStream getInputStream() throws IOException {
return modem.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
return modem.getOutputStream();
}
public boolean shouldFlush() {
return true;
}
public void dispose(boolean exception, boolean recognised) {
if(LOG.isLoggable(INFO)) LOG.info("Call disconnected");
try {
modem.hangUp();
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
exception = true;
}
if(exception) resetModem();
finished.countDown();
}
private void waitForDisposal() throws InterruptedException {
finished.await();
}
}
}

View File

@@ -0,0 +1,41 @@
package org.briarproject.plugins.modem;
import java.util.concurrent.Executor;
import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.util.StringUtils;
public class ModemPluginFactory implements DuplexPluginFactory {
private static final int MAX_FRAME_LENGTH = 1024;
private static final long MAX_LATENCY = 60 * 1000; // 1 minute
private static final long POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour
private final Executor pluginExecutor;
private final ModemFactory modemFactory;
private final SerialPortList serialPortList;
public ModemPluginFactory(Executor pluginExecutor,
ReliabilityLayerFactory reliabilityFactory) {
this.pluginExecutor = pluginExecutor;
modemFactory = new ModemFactoryImpl(pluginExecutor, reliabilityFactory);
serialPortList = new SerialPortListImpl();
}
public TransportId getId() {
return ModemPlugin.ID;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
// This plugin is not enabled by default
String enabled = callback.getConfig().get("enabled");
if(StringUtils.isNullOrEmpty(enabled)) return null;
return new ModemPlugin(pluginExecutor, modemFactory, serialPortList,
callback, MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL,
true);
}
}

View File

@@ -0,0 +1,23 @@
package org.briarproject.plugins.modem;
import java.io.IOException;
import jssc.SerialPortEventListener;
interface SerialPort {
void openPort() throws IOException;
void closePort() throws IOException;
boolean setParams(int baudRate, int dataBits, int stopBits, int parityBits)
throws IOException;
void purgePort(int flags) throws IOException;
void addEventListener(SerialPortEventListener l) throws IOException;
byte[] readBytes() throws IOException;
void writeBytes(byte[] b) throws IOException;
}

View File

@@ -0,0 +1,73 @@
package org.briarproject.plugins.modem;
import java.io.IOException;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
class SerialPortImpl implements SerialPort {
private final jssc.SerialPort port;
SerialPortImpl(String portName) {
port = new jssc.SerialPort(portName);
}
public void openPort() throws IOException {
try {
if(!port.openPort()) throw new IOException("Failed to open port");
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public void closePort() throws IOException {
try {
if(!port.closePort()) throw new IOException("Failed to close port");
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public boolean setParams(int baudRate, int dataBits, int stopBits,
int parityBits) throws IOException {
try {
return port.setParams(baudRate, dataBits, stopBits, parityBits);
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public void purgePort(int flags) throws IOException {
try {
if(!port.purgePort(flags))
throw new IOException("Failed to purge port");
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public void addEventListener(SerialPortEventListener l) throws IOException {
try {
port.addEventListener(l);
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public byte[] readBytes() throws IOException {
try {
return port.readBytes();
} catch(SerialPortException e) {
throw new IOException(e);
}
}
public void writeBytes(byte[] b) throws IOException {
try {
if(!port.writeBytes(b)) throw new IOException("Failed to write");
} catch(SerialPortException e) {
throw new IOException(e);
}
}
}

View File

@@ -0,0 +1,6 @@
package org.briarproject.plugins.modem;
interface SerialPortList {
String[] getPortNames();
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.plugins.modem;
class SerialPortListImpl implements SerialPortList {
public String[] getPortNames() {
return jssc.SerialPortList.getPortNames();
}
}