Sending and receiving GSM SMS on Blackberry using RIM APIs
One of the most interesting aspects of the mobile applications is the asynchronous messaging. SMS messages, despite their small size and other limitations represent a very important mechanism the applications can use for various purposes: sending small connectionless notifications from the client to the server, waking up the inactive application on the user's device, making payments, chunked data exchange (like used in Wireless Village protocol) etc. Unfortunately, the Wireless Messaging API (WMA API for MIDP) has very limited capabilities when it comes to customizing the message format and the payload. RIM Blackberry devices offer much richer (and not very well documented) access to the SMS features. This article explains how to control various parameters of the GSM SMS from Java ME application on these devices.
Quick links
- WMA way
- Sending SMS using RIM API
- Handling incoming SMS on Blackberry
- What is about the Push Registry?
As you know, sending SMS using WMA API is very easy. All you need is to open an SMS "client connection", then create a message object for BINARY or TEXT (and since WMA 2.0 - MULTIPART) message, put the payload into this object and send it. Something like this:
MessageConnection conn = (MessageConnection)Connector.open("sms://18001234567:5678"); BinaryMessage msgOut = conn.newMessage(MessageConnection.BINARY_MESSAGE); msgOut.setPayloadData("my binary payload".getBytes("UTF-8")); conn.send(msgOut);
And this is maximum you can do (which is enough in some cases). However, you never know how does this particular J2ME implementation will format your message and where it is going to be sent - all this is defined by the phone configuration, operator and the skill level of the developer who implemented this functionality.
Blackberry allows you to use WMA API just like on any MIDP 2.0 phone and also offers its own datagram-based API for dealing with the SMS.
Let define first what do we want to achieve:
- we want to send a binary single-part message
- we want to have customized the UDH (User Data Header)
- we want to set some SMS parameters like Message Class, Encoding, Delivery Period
First of all, unlike in WMA API, on Blackberry you have to use javax.microedition.io.DatagramConnection class for your connection instead of MessageConnection. I believe it does make sense since SMS message definitely fits into the definition of the datagram, at least from the developer's perspective.
DatagramConnection smsConnection = (DatagramConnection)Connector.open("sms://18001234567:9876"); // tricky part - creating SmsAddress object SmsAddress destinationAddr = new SmsAddress("//18001234567:9876");
The code quoted above uses some not-so-well-documented RIM classes. For example, net.rim.device.api.io.SmsAddress. Just read the javadocs for this class and you will understand what I am talking about. For example, one of the parameters for one of the methods is documented as "A string representing the SMS address in the format described in the SMSAddress class description.". Guess what you can find in the class description ;)
Next part is more straightforward. We just need to configure the SMS header. Your SmsAddress object (this is not obvious) actually contains the SMS header. net.rim.device.api.system.SMSPacketHeader class is a very interesting one, you will be impressed once you see the javadocs. The key phrase is the second (and the last sentence) in the class description - "See GSM 03.40 for details" This is the ETSI specification where you will find the detailed description of all the header fields in the SMS. We will stick to the original goal and just set a couple of parameters.
SMSPacketHeader header = destinationAddr.getHeader(); // no need for the report header.setStatusReportRequest(false); // we are going to use the UDH header.setUserDataHeaderPresent(true); // setting the validity and delivery periods header.setValidityPeriod(SMSParameters.PERIOD_INDEFINITE); header.setDeliveryPeriod(SMSParameters.PERIOD_INDEFINITE); // setting the message class header.setMessageClass(SMSParameters.MESSAGE_CLASS_1); // setting the message encoding - we are going to send UTF-8 characters so // it has to be 8-bit header.setMessageCoding(SMSParameters.MESSAGE_CODING_8_BIT);
There are some methods that you may find useful defined in the net.rim.device.api.io.DatagramConnectionBase class. You have to cast your DatagramConnection instance to get to it.
Now it is time to take care about the payload. Note: the UDH header is actually a part of the payload, not the SMS header. The only thing related to the UDH is the flag that the message DOES HAVE UDH (we have set it to "true"). Also, if you use UDH, you have to use 8-bit encoding.
int maxDatagramLength = smsConnection.getMaximumLength(); // note - I have seen this method returning some strange // values. If it returns anything bigger than 140 you have // to limit your message size to 140 bytes final byte myUDH = new byte[] { 0x06, 0x05, // application addressing scheme 0x04, // address size in bytes: 2 x 2 bytes 0x04, 0xd2, // destination port=1234, little-endian 0x04, 0xd2 // source port=1234, little-endian }; // calculating the remaining space for the data maxDatagramLength -= myUDH.lenth; byte[] message = "Bonjour de Montréal".getBytes("UTF-8"); // building the actual SMS payload byte[] completePayload = new byte[myUDH.length + message.length]; System.arraycopy(myUDH, 0, completePayload, 0, myUDH.length); System.arraycopy(message, 0, completePayload, myUDH.length, message.length);
The message is almost ready. All we need to do is to wrap it into a Datagram (javax.microedition.io.Datagram) and send it:
// allocating new datagram Datagram d = smsConnection.newDatagram(completePayload.length); // tricky part to set the destination, will not work without this call ((DatagramBase)d).setAddressBase(destinationAddr); // now adding the payload with the UDH to the datagram d.setData(completePayload, 0, completePayload.length); // and finally sending it smsConnection.send(d);
Of course, the code has to take care of all possible exceptions that may be thrown by these methods. Asynchronous I/O is still I/O.
### Handling incoming SMS on Blackberry
Receiving SMSes is much easier then sending, primarily since someone has already taken care of sending them properly - otherwise you will have nothing to receive :)
In WMA API you have two options for receiving asynchronous notifications: you can either register a listener that will be called every time something comes in (MessageConnection.setMessageListener()) or just calling MessageConnection.receive() from a separate thread. In either case it will cost you a thread so these options are not that much different.
When using Blackberry API everything starts with opening a "server" connection:
DatagramConnection incomingConnection = (DatagramConnection)Connector.open("sms://:1234"); // starting a thread to handle the reading ...
After that you need to just run a loop around the code like this:
while(...) { try { Datagram sms = incomingConnection.newDatagram( incomingConnection.getMaximumLength()); // ... // this call will block incomingConnection.receive(sms); byte[] payload = sms.getData(); } catch (IOException) { // ... } catch (SecurityException) { // ... } catch (Throwable t) { // ... } }
You can analyze the SMS headers using the same methods as we used when sending out the messages if you really want to make sure it is an SMS for your application. However, since only one application can open a port at the same time, this kind of verification would be extra.
Since I have written these paranoid "catch" blocks, I have to explain why did I do it. Unfortunately, I have to state that Blackberry SMS API is not reliable and at different points on different devices may demonstrate very strange behavior. So:
- You absolutely need to catch IOException. However, an IOException does not really indicate a permanent non-recoverable error. I would suggest to count the number of consecutive exceptions and if it exceeds certain threshold - close the connection and initiate self-destruct sequence for the device ;) Or at least to inform the user that the application cannot receive the notifications.
- SecurityException has to be handled in a special way. Unlike IOException, this exception indicates that the application is not allowed to receive the SMS (at least on this port). Do not ask me why you get it only after first attempt to receive a datagram, not after opening the server connection :) One more note about this exception: in some of the devices it has been replaced with an IOException with the message "Permission denied" which makes it even harder to write proper code…
- Throwable. I have seen the SMS connections throwing various exceptions - from NullPointerException to IllegalStateException. Usually it happens when the device state changes, especially when the device is restarted. I would suggest to apply the same pattern as for the IOException and consider certain threshold.
- Finally, it is highly recommended to put a wait() for at least a couple of hundreds of milliseconds after any error condition detected.
One more note about receiving the SMS and restarting the device. Some older versions of Blackberry firmware may attempt to push you the messages that you have already received and processed before. Especially if the SMSes are stored on the SIM card. You may need to implement a filter that will discard these duplicates.
### What is about the Push Registry?
In order to have the complete story I need to explain how to make sure that your application always receives the SMSes. This is important for any application since the SMS can be delayed for a couple of hours, arrive twice etc. And if there is no application is listening for the target port (assuming the SMS with UDH), it will go straight to the inbox, which is highly undesirable.
In MIDP you could use "Push Registry" mechanism that would "register" certain port number (possibly with the additional filter for the source phone number) with the platform so it would automatically start the application upon receiving the message matching the filter. By the way, do not try this method with Blackberries if you are writing MIDlets - most likely you will be surprised :(
Blackberry applications have to start automatically (this option is available for the application modules on Blackberry) when the device starts and run in the background until next reboot. Upon startup the application should open the incoming connection and then enter the loop waiting for the incoming messages. Here is a couple of advises for writing this kind of "daemons":
- Split your application into two parts. Small part (the "daemon" will be responsible just for the receiving of the SMSes. Once it receives a message and if the main part (UI-based) is not running it would start it. Then even if UI application exits completely (to release the memory) the application will still be ready to react to the incoming notifications.
- Make sure your "daemon" is 100% reliable, does not cause any infinite loops, does not crash and does not contain any memory leaks.
- You can exchange the data (even between the processes) on Blackberry by using net.rim.device.api.system.RuntimeStore.
- ETSI (European Telecommunications Standards Institute)
- Blackberry Developer Resources
- Wireless Messaging API (JSR-120, JAR-205) home
- RIM Device Java Library 4.1
blog comments powered by Disqus