Friday 30 September 2011

Android: Programmatically receiving SMS messags

As I`m sure you already know or at least suspect by now, it`s possible to programmatically "intercept" incoming short messages like SMS and/or MMS. All you need is the right permissions and a few lines of code.
Preparing the manifest file
The first thing we're going to do is to give the app permission to receive SMS messages. Just add the uses permission android.permission.RECEIVE_SMS using the manifest editor or the xml tag
 
<uses-permission android:name="android.permission.RECEIVE_SMS"> 

directly into the manifest file.

Now that we can receive SMS messages, we need to register an Intent Broadcast Rreceiver that will be called whenever a message arrives. More information about broadcast receivers can be found here.

Before we continue with the changes to the manifest file, let's talk about implementing our broadcast receiver class. Fortunately the framework already has a handy base class for that so we just need to extend it and override the proper method.

In our example we'll use the following class:

class MySMSReceiver extends BroadcastReceiver{
  @Override
  public void onReceiver(Context context, Intent intent){
    //do something with the message received
  }
}

To register a broadcast receiver, in the application section of your manifest file add

<receiver android:name=".MySMSReceiver "> 
    <intent-filter> 
            <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
    </intent-filter> 
</receiver> 

The most important thing to notice in the xml snippet above is android:name=".MySMSReceiver ". the name attribute sets the class to be instantiated when the broadcast for the intent is received. The "." is not a type, we're just telling android to find the class MySMSReceiver in the current package.

Also note that the receiver has an intent filter tag, meaning that only that type of intent will be passed to the intent receiver. Android will make sure the receiver code runs even if your app is not running.

Now let's see how to get the content of the the received message.

The SMS messages in Android use the PDU format (Protocol Description Unit) meaning that the content (sender, body, etc) is available in text mode albeit encoded. But fear not, the API can easily decode PDU data.

The message received by the broadcast receiver is available in the extras of the intent under the tag "pdus". Let's change our receiver to decode the first PDU of the message received

  class MySMSReceiver extends BroadcastReceiver{
  @Override
  public void onReceiver(Context context, Intent intent){
    Object[] pdus=(Object[])intent.getExtras().get("pdus");
    SmsMessage shortMessage=SmsMessage.createFromPdu((byte[]) pdus[0]);

    Log.d("SMSReceiver","SMS message sender: "+
       shortMessage.getOriginatingAddress());
    Log.d("SMSReceiver","SMS message text: "+
       shortMessage.getDisplayMessageBody());
  }
}

And there we have it! You can find more about the fields in the message in the SDK documentation. Remember that SMS messages can be relayed through email-to-sms relays, meaning that the sender can often be an email address and not a phone number.

Wait, did you notice that pdus is an array? That's because the SMS protocol allows for 160 characters long messages only. If the message is longer than that, several PDU objects will be created.
So to assemble a message broken down into several PDUs, just iterate the array:

...
    Object[] pdus=(Object[])intent.getExtras().get("pdus");
    String sender="";
    StringBuilder text=new StringBuilder();
    // get sender from first PDU
    SmsMessage shortMessage=SmsMessage.createFromPdu((byte[]) pdus[0]);
    sender=shortMessage.getOriginatingAddress();
    for(int i=0;i<pdus.length;i++){
      shortMessage=SmsMessage.createFromPdu((byte[]) pdus[i]);
      text.append(shortMessage.getDisplayMessageBody());
    } 
...

Next post I will talk about sending SMS messages, which is much easier  ;)

---
Programming tricks and tips for android developers