Android
Short Message Service

Modified

Resources

http://www.developershome.com/sms/ - Excellent discussion of SMS, including development tools/libraries/etc. for sending/receiving SMS on computer via cell-phone.

http://www.krvarma.com/2010/07/sending-and-receiving-sms-in-android/ - Source of the last example.

SMS messaging - Example and discussion of Android SMS

http://www.nowsms.com/index.htm SMS - Windows server with 60-day trial.

Testing

SMS messages can be sent between two emulators.

The simplest approach is to create two AVDs in Eclipse:

Window | Android SDK and AVD Manager | New

Create an AVD of a different name (e.g Phone1 and Phone2)

Debug on a specific AVD by:

Select the app in Project Explorer | Properties | Run/Debug Settings

Select Launch Configuration | Edit | Target | Select a different AVD for each emulation

Debug from main tool bar.

Android SMS

Below is a simple example using SMS to send a message from emulator 5558  and receive on emulator 5554, where number and message is hard-coded.

Testing

Run SMSreceive first, then SMSsend.

SMSsend sends the Hello World message to 5554.

The AndroidManifest.xml includes permission to send SMS messages.

<uses-permission android:name="android.permission.SEND_SMS"/>

SMSsend AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="edu.ius.rwisman.SMSsend"
     android:versionCode="1"  android:versionName="1.0">
     <uses-sdk android:minSdkVersion="13" />
     <application android:icon="@drawable/icon" android:label="@string/app_name">
          <activity android:name=".SMSsend" android:label="@string/app_name">
                <intent-filter>
                     <action android:name="android.intent.action.MAIN" />
                     <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
          </activity>
     </application>
     <uses-permission android:name="android.permission.SEND_SMS"/>
 </manifest>
 
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"  android:layout_width="fill_parent" android:layout_height="fill_parent">
   <TextView  android:layout_width="fill_parent"  android:layout_height="wrap_content"  android:text="@string/hello"/>
</LinearLayout>
 

To send an SMS message, use the SmsManager class, instantiate the class by the getDefault() static method to obtain an SmsManager object.

SmsManager sms = SmsManager.getDefault();

sendTextMessage() method sends the SMS message with a PendingIntent.

The Intent defines the operation to perform.

new Intent(this, SMSsend.class)

Constructor

Intent(Context packageContext, Class<?> cls) - Create an intent for a specific component.

The PendingIntent object identifies a target to invoke at a later time.

PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, SMSsend.class), 0 )

public static PendingIntent getActivity (Context context, int requestCode, Intent intent, int flags)

Retrieve a PendingIntent that will start a new activity, like calling Context.startActivity(Intent). Note that the activity will be started outside of the context of an existing activity, so you must use the Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent.

Parameters

context - The Context in which this PendingIntent should start the activity.
requestCode - Private request code for the sender (currently not used).
intent - Intent of the activity to be launched.
flags - May be FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT, or any of the flags as supported by Intent.fillIn() to control - which unspecified parts of the intent that can be supplied when the actual send happens.

Returns

Returns an existing or new PendingIntent matching the given parameters. May return null only if FLAG_NO_CREATE has been supplied.

One example use is after sending the message, a PendingIntent object can display another activity.

sms.sendTextMessage("5554", null, "Hello world", pi, null);

Method syntax is:

public void sendTextMessage (String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)

destinationAddress is the destination phone number

scAddress is the address of the service center (normally we pass null)

sentIntent is a broadcast which will be send when the SMS message is successfully sent from the device

deliveryIntent is also a broadcast sent when the SMS message is delivered to the destination address. The PendingIntent object (pi) is simply pointing to the same activity (SMSsend), so when the SMS is sent, nothing will happen.

On emulators, SMSsend sends repeatedly unless the PendingIntent is canceled by:

pi.cancel();

SMSsend.java
package edu.ius.rwisman.SMSsend;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;

public class SMSsend extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, SMSsend.class), 0 );   
             
        SmsManager sms = SmsManager.getDefault();

        sms.sendTextMessage("5554", null, "Hello world", pi, null);

        pi.cancel();
     }
}

SMSreceive is a little different in that it is not an Activity but registers to be executed only when an SMS message is received.

The AndroidManifest.xml must register the app to receive SMS messages.

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

and permit receiving SMS messages.

<uses-permission android:name="android.permission.RECEIVE_SMS"/>

SMSreceive AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="edu.ius.rwisman.SMSreceive"
     android:versionCode="1" android:versionName="1.0">
     <uses-sdk android:minSdkVersion="13" />
     <application android:icon="@drawable/icon" android:label="@string/app_name">
          <receiver android:name=".SMSreceive" android:enabled="true" android:exported="true"> 
                <intent-filter android:priority="999"> 
                     <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
                </intent-filter> 
          </receiver>
      </application>
      <uses-permission android:name="android.permission.RECEIVE_SMS"/>
</manifest>

SMSreceive inherits from BroadcastReceiver is not an Activity and does not have a UI.

onReceive() is called when an SMS message is received because SMSReceive registered in the AndroidManifest.xml.

The SMS message is part of the intent extras passed in a Bundle to onReceive() method. A (pdu) contains the SmsMessage.

Object[] pdus = (Object[]) bundle.get("pdus");

SmsMessage.createFromPdu() is a static class method to cast pdu's to SmsMessage's.

msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);

SMS messages may be too large (more than 141 characters) and be broken into multiple messages.

The received message is displayed temporarily to the current screen by:

Toast.makeText(context, str, Toast.LENGTH_SHORT).show();

SMSreceive.java
package edu.ius.rwisman.SMSreceive;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;

public class SMSreceive extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent)  {
    
        this.abortBroadcast();    
    
        SmsMessage[] msgs = null;
        String str = "";  
        Bundle bundle = intent.getExtras();        			// SMS message passed
  
        if (bundle != null) {
            
            Object[] pdus = (Object[]) bundle.get("pdus");	// SMS message received
            msgs = new SmsMessage[pdus.length];
            
            for (int i=0; i<msgs.length; i++) {
                msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);                
                str += "SMS from " + msgs[i].getOriginatingAddress();                     
                str += " :";
                str += msgs[i].getMessageBody().toString();
                str += "\n";        
            }
            
            Toast.makeText(context, str, Toast.LENGTH_SHORT).show();    //display SMS
        }                         
    }
}

Received SMS messages also are stored in the SMS database by the default SMS message system. onReceive() may be called before or after the messages are stored in the database.

One purported solution is to ensure your own SMS receiver is called first, then abort the broadcast of the SMS message to other BroadcastReceivers.

What is said about ordered broadcasts:

Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.

AndroidManifest.xml sets the priority of SMSreceive to 999, hopefully high enough to be the first to receive the android.provider.Telephony.SMS_RECEIVED.

This solution does not work on emulation, the default SMS message system still receives the message and stores in its database, maybe it works correctly on a real device.

 

BroadcastReceiver as an inner-class

Video

Essentially the same as above SMS broadcast receiver except implemented as an inner-class within an activity.

Probably more useful as a user interface could be provided.

Note that the BroadcastReceiver is registered with an IntentFilter("android.provider.Telephony.SMS_RECEIVED").

SMSreceiver.java
package edu.ius.rwisman.SMSreceiver;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;

public class SMSreceiver extends Activity {
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
        		super.onCreate(savedInstanceState);
        		setContentView(R.layout.main);
        		registerReceiver(smsreceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
    	}
    
    	@Override
	protected void onDestroy() {
    		unregisterReceiver(smsreceiver);		
		super.onDestroy();
	}
	
    	private BroadcastReceiver smsreceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
	        		Bundle bundle = intent.getExtras();        
	        		SmsMessage[] msgs = null;
	        
	        		if(null != bundle)  {
	        			String info = "Text SMS from ";
	            		Object[] pdus = (Object[]) bundle.get("pdus");
	            		msgs = new SmsMessage[pdus.length];
	            
	            		for (int i=0; i<msgs.length; i++) {
	               			msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);                
	                			info += msgs[i].getOriginatingAddress()+"\n";                     
	                			info += msgs[i].getMessageBody().toString();
	            		}
	            		Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
	        		}                         
		}
	};
}
SMSreceiver AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="edu.ius.rwisman.SMSreceiver"
	android:versionCode="1" android:versionName="1.0" >
	<uses-permission android:name="android.permission.READ_SMS"/>
	<uses-permission android:name="android.permission.RECEIVE_SMS"/>
	<uses-sdk android:minSdkVersion="13" />

	<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" >
	    <activity android:label="@string/app_name" android:name=".SMSreceiver" >
		<intent-filter >
			<action android:name="android.intent.action.MAIN" />
			<category android:name="android.intent.category.LAUNCHER" />
		</intent-filter>
	   </activity>
	</application>
</manifest>

 

More about SMS and BroadcastReceivers

The following example illustrates:

When a text message SMS is received by a device, the following Intent is broadcast:

android.provider.Telephony.SMS_RECEIVED

A BroadcastReceiver can register to receive the broadcast by:

registerReceiver(smsreceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));

where smsreceiver is:

private BroadcastReceiver smsreceiver = new BroadcastReceiver() {
@Override
    public void onReceive(Context context, Intent intent) {
    }
};

IntentFilter("android.provider.Telephony.SMS_RECEIVED") defines the broadcast Intent smsreceiver should receive.
 

Download

Video

SMSDemo.java
package edu.ius.rwisman.SMSDemo;

import java.util.ArrayList;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

public class SMSDemo extends Activity {
    private static final int MAX_SMS_MESSAGE_LENGTH = 160;
	private static final int SMS_PORT = 8091;
	private static final String SMS_DELIVERED = "SMS_DELIVERED";
	private static final String SMS_SENT = "SMS_SENT";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ((Button)findViewById(R.id.send)).setOnClickListener(onButtonClick);
        
        registerReceiver(sendreceiver, new IntentFilter(SMS_SENT));
        registerReceiver(deliveredreceiver, new IntentFilter(SMS_DELIVERED));       
        registerReceiver(smsreceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
    }
    
    @Override
	protected void onDestroy() {
    		unregisterReceiver(sendreceiver);
    		unregisterReceiver(deliveredreceiver);
    		unregisterReceiver(smsreceiver);		
		super.onDestroy();
	}

	private void sendSms(String phonenumber,String message, boolean isBinary) {
    		SmsManager manager = SmsManager.getDefault();   	
											// Received internally
    		PendingIntent piSend = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0);
    		PendingIntent piDelivered = PendingIntent.getBroadcast(this, 0, new Intent(SMS_DELIVERED), 0);
    	
    		if(isBinary) {
	    		byte[] data = new byte[message.length()];	    	
	    		for(int index=0; index<message.length() && index < MAX_SMS_MESSAGE_LENGTH; ++index)
	    			data[index] = (byte)message.charAt(index);
	    	
	    		manager.sendDataMessage(phonenumber, null, (short) SMS_PORT, data, piSend, piDelivered);
    		}
    		else {
    			int length = message.length();    		
    			if(length > MAX_SMS_MESSAGE_LENGTH) {
    				ArrayList<String> messagelist = manager.divideMessage(message); 			
    				manager.sendMultipartTextMessage(phonenumber, null, messagelist, null, null);
    			}
    			else
    				manager.sendTextMessage(phonenumber, null, message, piSend, piDelivered);
    		}
    	}
    
    	private View.OnClickListener onButtonClick = new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			switch(v.getId()) {
				case R.id.send: {
					String phonenumber = ((TextView)findViewById(R.id.phonenumber)).getText().toString();
					String message = ((TextView)findViewById(R.id.message)).getText().toString();
					boolean isBinary = ((CheckBox)findViewById(R.id.binary)).isChecked();
					
					sendSms(phonenumber,message, isBinary);					
					break;
				}
			}
		}
	};
	
	private BroadcastReceiver sendreceiver = new BroadcastReceiver() {	// SMS_SENT
		@Override
		public void onReceive(Context context, Intent intent) {
			String info = "Send information: ";
			
			switch(getResultCode()) {
				case Activity.RESULT_OK: info += "send successful"; break;
				case SmsManager.RESULT_ERROR_GENERIC_FAILURE: info += "send failed, generic failure"; break;
				case SmsManager.RESULT_ERROR_NO_SERVICE: info += "send failed, no service"; break;
				case SmsManager.RESULT_ERROR_NULL_PDU: info += "send failed, null pdu"; break;
				case SmsManager.RESULT_ERROR_RADIO_OFF: info += "send failed, radio is off"; break;
			}			
			Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show();
		}
	};
	
	private BroadcastReceiver deliveredreceiver = new BroadcastReceiver() {	// SMS_DELIVERED
		@Override
		public void onReceive(Context context, Intent intent) {
			String info = "Delivery information: ";
			
			switch(getResultCode()) {
				case Activity.RESULT_OK: info += "delivered"; break;
				case Activity.RESULT_CANCELED: info += "not delivered"; break;
			}			
			Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show();
		}
	};
	
	private BroadcastReceiver smsreceiver = new BroadcastReceiver() {		// android.provider.Telephony.SMS_RECEIVED
		@Override
		public void onReceive(Context context, Intent intent) {
	        		Bundle bundle = intent.getExtras();        
	        		SmsMessage[] msgs = null;
	        
	        		if(null != bundle)  {
	        			String info = "Text SMS from ";
	            		Object[] pdus = (Object[]) bundle.get("pdus");
	            		msgs = new SmsMessage[pdus.length];
	            
	            		for (int i=0; i<msgs.length; i++) {
	                			msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);                
	                			info += msgs[i].getOriginatingAddress();                     
	                			info += "\n*****TEXT MESSAGE*****\n";
	                			info += msgs[i].getMessageBody().toString();
	            		}
	            		Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
	        		}                         
		}
	};
}

Binary SMS messages are received on port 8091 designated in the AndroidManifest.xml file by:

      <receiver android:name=".BinarySMSReceiver">
          <intent-filter>
               <action android:name="android.intent.action.DATA_SMS_RECEIVED"/>
               <data android:port="8091"/>
               <data android:scheme="sms"/>
          </intent-filter>
       </receiver>
 

BinarySMSReceiver.java
package edu.ius.rwisman.SMSDemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;

public class BinarySMSReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		Bundle bundle = intent.getExtras();        
        		SmsMessage[] msgs = null;
        
        		if(null != bundle) {
        			String info = "Binary SMS from ";
            		Object[] pdus = (Object[]) bundle.get("pdus");
            		msgs = new SmsMessage[pdus.length];
            		byte[] data = null;
            
            		for (int i=0; i<msgs.length; i++){
               			msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);                
                			info += msgs[i].getOriginatingAddress();                     
                			info += "\n*****BINARY MESSAGE*****\n";             
                			data = msgs[i].getUserData();              
                			for(int index=0; index<data.length; ++index)
                				info += Character.toString((char)data[index]);
            		}
            		Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
        		}
	}
}

 

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="edu.ius.rwisman.SMSDemo" android:versionCode="1" android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
       <activity android:name=".SMSDemo" android:label="@string/app_name">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
      </activity>
      <receiver android:name=".BinarySMSReceiver"> 
          <intent-filter> 
               <action android:name="android.intent.action.DATA_SMS_RECEIVED"/>
               <data android:port="8091"/>
               <data android:scheme="sms"/> 
          </intent-filter> 
       </receiver>
    </application>
    <uses-sdk android:minSdkVersion="13" />
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
    <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
</manifest> 		

 

main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:padding="10dip">

<LinearLayout
	android:orientation="vertical"
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content">

  <TextView 
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:text="@string/phoneno_info"/>

  <EditText
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content"
	android:id="@+id/phonenumber"
	android:imeOptions="actionNext"
	android:inputType="phone">
  </EditText>

  <TextView 
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:text="@string/message_info"/>

   <EditText
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content"
	android:id="@+id/message"
	android:lines="5"
	android:gravity="top"
	android:imeOptions="actionDone"
	android:inputType="textMultiLine|textCapSentences">
  </EditText>

  <CheckBox
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content"
	android:text="@string/send_binarysms"
	android:checked="false"
	android:id="@+id/binary">
  </CheckBox>

  <Button
	android:layout_width="wrap_content" 
	android:layout_height="wrap_content"
	android:layout_gravity="right"
	android:id="@+id/send"
	android:text="Send"
	android:paddingLeft="30dip"
	android:paddingTop="10dip"
	android:paddingRight="30dip"
	android:paddingBottom="10dip">
  </Button>

</LinearLayout>
</ScrollView>

 

strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="phoneno_info">Phone Number</string>
   <string name="app_name">SMS Demo</string>
   <string name="message_info">SMS Message</string>
   <string name="send_binarysms">Send binary sms (to port 8091)</string>
</resources>