An SMS Based Communication Framework in Java

By Jonas Thomsen (jones@daimi.au.dk).

Note!

This report has been modified on September 10, 2007 in order to reflect recent changes in supported phones and required third-party software. Sun have decided to no longer support JavaComm 2.0 and the Microsoft Windows platform. See further details in the Serial Communication in Java section.

This report has been modified on November 30, 2005 in order to reflect recent changes in the SMS library code. In order to use all the features mentioned here, you need to have the latest version of the JavaLib-file, which can be downloaded in the download section.

Content

Introduction

Since the introduction of the GSM networks in 1992, SMS has been a service with an increasing interest. SMS is actually not designed for the success it is having today. The marketing guys within the standardization group could not see any use of a service, requiring that the user should press the same key several times to write a character. The only reason for SMS became part of the standard, was the techical guys wanting it for service messages and testing.

Today, SMS is an extensively used service and almost everyone is able to use it. This has lead to an increasing number of applications using SMS as the interface to the user. This project contains an SMS framework, written in Java. The framework is designed for robustness in order to serve as a gateway between users with their mobile phone, and the application. The implemented application is an SMS-to-OSC gateway, sending all incomming SMS' to an OSC capable host. OSC is expained in greater detail in section OSC.

Throughout the rest of this paper, we first describe what SMS is from a technical point of view. Next we discuss some aspects of serial communication in general and in Java. Then we go into the details of the SMS framework and how it can be used in an application. We then present OSC and the Java implementation of OSC. Finally we present the SMS2OSC application.

It is assumed that the reader is familiar with general Java programming.

Description of SMS

Short Message System (SMS) is a part of the Global System for Mobile Communications (GSM) specification. GSM is initially specified by European Telecommunications Standards Institute (ETSI - http://www.etsi.org). The current specification is controlled by 3rd Generation Partnership Project (3GPP - http://www.3gpp.org), from where the specification can be downloaded. Note, it is huge!

In the first phase of the GSM networks, SMS reception was mandatory for a mobile phone (called Mobile Station (MS) in the specification), but sending SMS was an optional feature. From phase 2 of the GSM networks (1997), sending SMS became mandatory. The current networks are all phase 2 or later.

SMS is a no-guaranties service. The operator of the GSM network does not give any guaranties for delivery of SMSs. Because of this, SMS is a costless service for the operator, as messages can be sent during idle periods of the network.
An SMS is just a single datapacket or Protocol Data Unit (PDU) in the network, but PDUs cannot be sent without a connection. In order to send an MS originated SMS, the MS creates a connection to the Base Transceiver Station (BTS), sends the PDU containing the SMS, and finally closes the connection. Because an SMS is limited to one single PDU, it can only contain 160 characters. Modern MSs are able to send longer SMSs, called Extended Message System (EMS), which just are several ordinary SMSs displayed as one on the MS.

Serial Communication in Java

In order to use SMS in a computer application, an interface between the computer and the GSM network is needed. This interface is typically an MS connected to a serial port on the computer. In this project the serial connection to an MS is the interface.

In Java, serial communication is specified in the package javax.comm, which is a part of the extended specification (the javax). Because it is part of the extended specification, it it not part of the reference Java implementation from Sun (http://java.sun.com). Sun has only implemented javax.comm for Microsoft Windows and Sun's Solaris/SPARC platform (see http://java.sun.com/products/javacomm/). Implementations for other platforms exist, e.g. the RxTx project (http://www.rxtx.org), which provides the javax.comm package for Linux, MacOS X, etc.

Update: Sun has released a new version 3 of the JavaComm API. This version is not tested with the framework presented here. Furthermore, Sun has no longer support for the Microsoft Windows platform and the JavaComm API is no longer available for download for Microsoft Windows in version 2. I have version 2 for Windows and it can be downloaded in the download section.

When an application is communicating through a serial interface, it can basicly do this in two ways: It can poll the serial port for data (synchronously), and it can receive data events (something like interrupts) when data is received (asynchronously). The javax.comm allows you to do both. Synchronous communication is done by using the read methods on the InputStream of the serial port. If there is no data, the method will wait (block) until data is available or a timeout occurs. The timeout value can be set by the SerialPort.enableReceiveTimeout(int). Asynchronously communication is done by implementing the SerialPortEventListener interface. This requires implementation of the method serialEvent(SerialPortEvent event). It is possible to specify which kind of serial events the application wants, by using the SerialPort.notifyOn*() methods.

When using serial ports in Java, it is very important to close the port when the class has finish using it. If the port is not closed correctly, no other application will be able to use it. In an environment where the Java Virtual Machine (JVM) is running for a long time, e.g. in an application server, forgetting to close the serial port might result in the need for a restart of the JVM (and thereby the application server) in order to release the serial port.

Installing the Sun javax.comm on Microsoft Windows has a minor detail not described in the documentation. The file javax.comm.properties has to be in the folder [JavaSDK]\jre\lib - it is not enough to put it in [JavaSDK]\lib. If your JavaSDK is in C:\j2sdk1.4.1_01, then you have to put the javax.comm.properties in C:\j2sdk1.4.1_01\jre\lib.

I have included a small test application for the SMS framework, which tests the serial communication. Please note that the test requires the SMS framework - it is not just a serial test. The source can be downloaded here: TestComm.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class Basic {
	public static void main(String[] args) {
		dk.daimi.jones.debug.Debugger.getDebugger().enableDebug();
		SMS sms = new SMSImpl();
		System.out.println("Testing SMS service...");
		if (((SMSImpl)sms).open("COM1", "")) { 
			System.out.println("SMS service test was succesful!");
		} else {
			System.out.println("SMS service test FAILED!");
		}
		((SMSImpl)sms).close();
	}
}

A Top-Down Description of the SMS framework

In this section, we describe the SMS framework in a top-down manner, i.e. we start the presentation of the framework from the application using it, then we present the SMS Interface and its implementation, and finally we present the lowest layer - the modified jSMSEngine.

The Application

Writing an application using the SMS framework is really simple. The following code shows a basic application using the SMS framework, though only initializing the framework and not doing anything interesting. The source can be downloaded here: Basic.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class Basic {
	public static void main(String[] args) {
		SMS sms = new SMSImpl();
		System.out.print("Starting SMS service... ");
		if (((SMSImpl)sms).open("COM1", "")) { 
			System.out.println("Done!");
		} else {
			System.out.println("Failed!");
		}
		((SMSImpl)sms).close();
	}
}

The next example of an application is retrieving information about the attached MS. The information is the manufacturer, the model, and the IMEI number, which is a unique serial number for the MS. The IMEI number is used for tracking stolen MSs. The source can be downloaded here: ShowProperties.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class ShowProperties {
	public static void main(String[] args) {
		SMS sms = new SMSImpl();
		System.out.print("Starting SMS service... ");
		if (((SMSImpl)sms).open("COM1", "")) { 
			System.out.println("Done!");
			System.out.println("SMS Device info:");
			System.out.println("----------------");
			System.out.println("Manufacturer: " + sms.getManufacturer());
			System.out.println("Model.......: " + sms.getModel());
			System.out.println("IMEI........: " + sms.getIMEI());
		} else {
			System.out.println("Failed!");
		}
		((SMSImpl)sms).close();
	}
}

The last example given here, shows how to send an SMS. The applications sends the SMS to the (probably) fictive phone number +45 12 34 56 78. It is important to add the country code to the phone number, i.e. +45 for Denmark. The source can be downloaded here: SendSMS.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class SendSMS {
	public static void main(String[] args) {
		SMS sms = new SMSImpl();
		System.out.print("Starting SMS service... ");
		if (((SMSImpl)sms).open("COM1", "")) { 
			System.out.println("Done!");
			try {
				sms.send(new SMSMessage("+4512345678", "Hello Phone!"));
			} catch (SMSException e) {
				System.out.println("Failed to send SMS");
			}
		} else {
			System.out.println("Failed!");
		}
		((SMSImpl)sms).close();
	}
}

It should by now be clear, that writing an application with SMS capabilities is quite simple, when using the SMS framework. In the next section we take a closer look at the SMS interface and its implementation.

The SMS Interface

The previously shown applications uses the SMS interface. We have made a general interface for specifying the minimum requirements for a simple SMS framework. The SMS interface is shown here:

public interface SMS {
	public abstract void send(SMSMessage smsmessage)
		throws SMSException;

	public abstract void addNMIListener(NMIListener nmilistener);

	public abstract void removeNMIListener(NMIListener nmilistener);
	
	public abstract String getManufacturer();
	public abstract String getModel();
	public abstract String getIMEI();
}

The SMS interface does not specify anything about the communication with the MS, e.g. which serial port the MS is attached to. The implementation of the interface specifies this. The reason for this separation is, that one might want a completely different backend for an USB connected MS, where the name of the serial port is irrelevant. One would then have to write another implementation of the SMS interface, alowing the user to specify an USB port in stead of a serial port.

Most of the SMS interface is pretty self explaing, except for the NMIListener stuff. When the SMS framework receives an SMS, it notifies all registred NMIListeners with the received SMS. If an application wants to receive incomming SMSs, it must register an implementation of the NMIListener interface with the framework. This registration is done by calling the addNMIListener method. The following code gives an example on using NMIListeners, and it can be downloadet here: ReceiveSMS.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class ReceiveSMS {

	public static void main(String[] args) {
		// Anonousmus class for implementing the NMIListener interface
		NMIListener myNMIListener = new NMIListener() {
			public void messageReceived(SMSMessage smsMessage) {
				System.out.println("Received SMS from '" + smsMessage.getAddress() + "'");
				System.out.println("Text: '" + smsMessage.getData() + "'");
			}
		};
		SMS sms = new SMSImpl();
		System.out.print("Starting SMS service... ");
		if (((SMSImpl)sms).open("COM1", "")) { 
			System.out.println("Done!");
			// Register reception of incomming messages with the framework
			sms.addNMIListener(myNMIListener);
			// Keep the application running so we can see the result
			try {
				while (true) {
					Thread.sleep(100);
				}
			} catch( Exception e ) {}
			
		} else {
			System.out.println("Failed!");
		}
		((SMSImpl)sms).close();
	}
}

The name 'NMI' is short for 'New Message Indication' and comes from the command being sent to the MS, telling it to notify the computer when messages arrive. This is described in the section New Message Indication.

The SMS Implementation - SMSImpl

All the applications previously shown, cast the variable sms to the class SMSImpl, when they open or close the framework. The SMSImpl is the implementation of the SMS interface, designed for using the modified jSMSEngine (described in section The modified jSMSEngine Package) as backend for communication with the MS. The SMSImpl class has the previously seen open and close methods and of couse all methods from the SMS interface as it is implementing this interface. The open method takes two parameters: port and pin. Port is the name of the serial port to which the MS is attached. Pin is the Pin code for the SIM card in the MS. The pin code is needed, if the MS has been detached from the GSM network, i.e. if it has been powered off. If the MS is a normal mobile phone, this parameter will be given to the phone by the user, when it is powered on. If the MS is some stationary GSM equipment, there might not be any keyboard on the MS, so the pin has to be given through the application.

The SMSImpl is also responsible for logging SMS activity on the console. When a class register itself as an NMIListener, a [+] followed by the registred class is written to the console. When a class is removed as an NMIListener, a [-] followed by the removed class is written. Incoming messages are written as [<] followed by phone number and text. Outgoing messages are written with a [>] in the beginning of the line. If an error occurs, the line starts with [E].

Besides being a wrapper for the backend jSMSEngine, the SMSImpl is also responsible for controlling concurrent access to the MS. If several threads are trying to send SMSs at the same time, it would crash the framework. By controlling the access to the send method with semaphores, the framework is threadsafe and thereby scaleable for environment where several services are using SMS as their communication medium.

The modified jSMSEngine Package

Below the nice SMS interface and the implementation of it, is the real SMS kernel. This is based on the jSMSEngine (http://www.jsmsengine.org). It is however a modified version of the jSMSEngine. In this section we describe our modifications of the jSMSEngine, but we do not describe the jSMSEngine in total - for this we refer to the documentation for jSMSEngine.

Low level SMS communications involves quite complicated code. Most MSs exspect that the application creates a ready to send PDU containing the SMS. Creating a PDU is a lot of bitnibling and though hard to code and not very interesting. This part of the jSMSEngine is left as the author wrote it. Our modifications are on the low level serial communication layer.

Handling Incoming Messages

As mentioned in the section Serial Communication in Java, serial communication can be synchronous or asynchronous. The same is the case for notifying a computer when an SMS is received. An MS is able to notify the computer in three ways: No notification, address in MSs memory where the message is stored, or dump the received message directly on the serial port. The original jSMSEngine used the first approach. This worked fine on one MS (Nokia 30), but not on other MSs (Sony Ericsson). Sony Ericsson phones do not allow an application to read messages from its memory. In order to use such phones (I have one myself), the last approach was necessary.

Allowing the MS to send data on the serial port whenever a message arrives, gives rise to some problems. If a command has been sent from the computer to the MS and the computer waits for an answer, an incoming SMS might be interleaved with the answer. To avoid this problem, we rewrote the serial communication module to handle incoming messages so they are not interleaved with answers given to commands.

New Message Indication

In order to get the MS to dump incoming messages on the serial interface, a 'New Message Indication' command (AT+CNMI) has to be sent to the MS. The parameters for this command varies from vendor to vendor. On a Nokia 30 MS, the full command is AT+CNMI=1,2,0,0,0 but on a Sony Ericsson MS the command is AT+CNMI=3,3,0,0,0. The two very different parameters, makes the MSs behave in the required way. Because different vendors accept so different parameters, we wrote code for querying the MS for its NMI capabilities and from that information setting the parameters correct to get the MS to dump messages immediatly.

Limiting functionality

The last modification to the jSMSEngine was to limit its external functionality. It has an XML based phonebook, which requires access to writing files to the disc. This is not always allowed and it gave rise to more problems than it helped. This part has been removed.

Troubleshouting - Debugging [new feature]

In order to troubleshout the communication with the MS, I have made a debugging feature available. This feature is used in the TestComm.java test application. All you have to do to get the debugging information from the framework, it to add the following line of code to your application before you start using the framework:

dk.daimi.jones.debug.Debugger.getDebugger().enableDebug();

You can also use the debugging functionality in you own code. In stead of using System.out.println(...) when you want to display debugging information, you can use my Debugger-class. The interface is displayed here (details are available in the Debugger.java file):

public final class Debugger {
	/* Returns (singleton) Debugger for this process. */
	public static Debugger getDebugger();
	public void enableDebug();
	public void disableDebug();
	public void debug(String s);
}

The following code illustrates how to use the debugging functionality:

ímport dk.daimi.jones.debug;
...
Debugger myDebug = dk.daimi.jones.debug.Debugger.getDebugger();
...
myDebug.debug("Debugging text is only visible if debugging is enabled");

OSC

Open Sound Control (OSC - http://cnmat.cnmat.berkeley.edu/OSC/ is a relatively new protocol exspected to replace the elderly Musical Instrument Digital Interface (MIDI) standard. OSC is based on UDP packets being sent between Internet attached devices. An UDP packet can contain up to 64 KB in theory, but should not exceed 8 KB i praxis. The idea of using UDP for sending information between musical instruments in this way is fine - it allows instruments to communicate over the Internet. On the other hand might it be a problem, because UDP packets might get lost in transit. There are no upper bound for the time taken to transmit an UDP packet, which might be a problem if the protocol is going to be used to synchronize instruments.

The OSC protocol exits in two versions: With basic type tags and with aditional type tags. Type tags are symbols representing the type of some data i the packet. The problem is, that with basic type tags, the symbol 's' means a string but with additional type tags, 'S' is a string and 's' means a symbol. This is a very bad design giving rise to a lot of problems, when finding out what data is included in the packet.

Java OSC

We have used a half-written library called Java OSC (http://www.mat.ucsb.edu/%7Ec.ramakr/illposed/javaosc.html). This labrary allows us to send OSC messages, but not to receive. The library is actually not a library, but an application, so we have 'converted' it to a library.

The SMS2OSC Application

The final application is based on the previously described components: The SMS framework and the Java OSC library. When combining the two by making an NMIListener that sends an OSC message to a specified host, containing the received SMS, we have an SMS-to-OSC application. It is really that easy!

The SMS2OSC NMIListener is shown below, and it can be downloade here: SMS2osc.java

import dk.daimi.jones.services.sms.*;
import com.illposed.osc.*;
import java.net.*;

public class SMS2osc implements NMIListener {

	private static OscPort oscPort = null;
	private static String oscMessageText = "/sms";

	public SMS2osc(String portName, int portNumber) {
		try {
			System.out.print("Setting OSC port to " + portName + ":" + portNumber + "... ");
			oscPort = new OscPort(InetAddress.getByName(portName), portNumber);
			System.out.println("Done!");
		} catch (Exception e) {
			System.out.println("Failed!");
			oscPort = null;
		}
	}
	
	public void messageReceived(SMSMessage smsMessage) {
		String from = smsMessage.getAddress();
		String message = smsMessage.getData();
		if (oscPort != null) {
			Object[] oscArgs = {from, message};
			OscSuperColliderMessage msg = new OscSuperColliderMessage(oscMessageText, oscArgs);
			try {
				oscPort.send(msg);
			} catch (Exception e) {
				System.out.println("Could not send OSC message:\n" + e.getMessage());
				e.printStackTrace();
			}
		}
	}
}

The main application for running the SMS-to-OSC service is shown below, and the source can be downloaded here: SMSServer.java

import dk.daimi.jones.services.sms.*;
import dk.daimi.jones.impl.sms.*;

public class SMSServer {

	private static SMS sms;
	
	public static SMS getSMS() {
		return sms;
	}
	
	public SMSServer () throws Exception {
			System.out.print("Creating SMS service... ");
			sms = new SMSImpl();
			if (sms == null) {
				System.out.println("Failed!");
				throw new Exception("Failed to create SMS service!");
			} else {
				System.out.println("Done!");
			}
			System.out.print("Starting SMS service... ");
			if (((SMSImpl)sms).open("COM1", "")) { 
				System.out.println("Done!");
			} else {
				System.out.println("Failed!");
				throw new Exception("Failed to start SMS service!");
			}
	}
	
	public static void main(String args[]) {
		String portName = "";
		int portNumber = 0;
		if (args.length >= 2) {
			portName = args[0];
			portNumber = Integer.parseInt(args[1]);
		} else {
			System.out.println("Usage: SMSServer  ");
			System.exit(1);
		}
		try {
			new SMSServer();
		} catch( Exception e ) {
			System.exit(2);
		}
		SMS2osc sms2osc = new SMS2osc(portName, portNumber);
		SMSServer.getSMS().addNMIListener(sms2osc);
		try {
			while (true) {
				Thread.sleep(100);
			}
		} catch( Exception e ) {
			e.printStackTrace();
		}
	}

}

Tested hardware

The SMS framework has been tested with to following MSs:

References

Throughout this paper we refer to external documents. In this section we give some additional references.

Communication with an MS through a serial interface is done by the Extended AT Command Set. This is documented in the GSM specification, but the different vendors have their own interpretation. The following are downloaded for your convenience:

Download

The source code for the entire project can be downloaded here: