There’ll be times when you’ll want to allow the users to send SMS messages directly from your app to other numbers (destination). The Android SDK does support to capability of sending SMS/MMS messages in two ways (from your app):
- Invoke an SMS client app via Implicit Intents.
- Send the SMS directly yourself by using the
SmsManager
telephony class API.
We’ll delve into both the approaches.
What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.
Using Implicit Intents
Using Android implicit intents we can display a list of SMS client apps that the user already has on his phone that he can use. Obviously if they have only one app (which is generally the default Messaging app unless other apps registered themselves for these intents using intent filter) then that’ll open up directly without presenting the user with a dialog filled with a list of choices that can be made. We can either use the ACTION_SENDTO
action or ACTION_VIEW
.
Let’s see an example where ACTION_VIEW
is used:
String phoneNumber = "9999999999"; String smsBody = "This is an SMS!"; Intent smsIntent = new Intent(Intent.ACTION_VIEW); // Invokes only SMS/MMS clients smsIntent.setType("vnd.android-dir/mms-sms"); // Specify the Phone Number smsIntent.putExtra("address", phoneNumber); // Specify the Message smsIntent.putExtra("sms_body", smsBody); // Shoot! startActivity(smsIntent);
With that piece of code, if you now test your app on your device, your Messaging app should be invoked with the number (or name if the number exists in your contacts) and message pre-populated. Amazing, isn’t it! Now we’ll see an example where ACTION_SENDTO
is used:
String phoneNumber = "9999999999"; String smsBody = "This is an SMS!"; // Add the phone number in the data Uri uri = Uri.parse("smsto:" + phoneNumber); // Create intent with the action and data Intent smsIntent = new Intent(Intent.ACTION_SENDTO, uri); // smsIntent.setData(uri); // We just set the data in the constructor above // Set the message smsIntent.putExtra("sms_body", smsBody); startActivity(smsIntent);
Using the SmsManager API
With the android.telephone.SmsManager
class, we can send an SMS automatically in two lines of API code.
String phoneNumber = "9999999999"; String smsBody = "Message from the API"; // Get the default instance of SmsManager SmsManager smsManager = SmsManager.getDefault(); // Send a text based SMS smsManager.sendTextMessage(phoneNumber, null, smsBody, null, null);
This method requires the SEND_SMS
permission which you should request in the manifest file.
<uses-permission android:name="android.permission.SEND_SMS" />
The first argument passed to sendTextMessage()
is the destination address to which the message has to be sent and the second argument is the SMSC address (it’s also like a phone number generally) to which if you pass null, the default service center of the device’s carrier will be used. Third argument is the text message to be sent in the SMS. The fourth and fifth arguments if not null must be pending intents performing broadcasts when the message is successfully sent (or failed) and delivered to the recipient.
Let’s see an example where we set broadcast receivers for when the message is sent successfully and is also delivered (or not).
String phoneNumber = "9999999999"; String smsBody = "This is an SMS!"; String SMS_SENT = "SMS_SENT"; String SMS_DELIVERED = "SMS_DELIVERED"; PendingIntent sentPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0); PendingIntent deliveredPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_DELIVERED), 0); // For when the SMS has been sent registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(context, "SMS sent successfully", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(context, "Generic failure cause", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NO_SERVICE: Toast.makeText(context, "Service is currently unavailable", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(context, "No pdu provided", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(context, "Radio was explicitly turned off", Toast.LENGTH_SHORT).show(); break; } } }, new IntentFilter(SMS_SENT)); // For when the SMS has been delivered registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS delivered", Toast.LENGTH_SHORT).show(); break; case Activity.RESULT_CANCELED: Toast.makeText(getBaseContext(), "SMS not delivered", Toast.LENGTH_SHORT).show(); break; } } }, new IntentFilter(SMS_DELIVERED)); // Get the default instance of SmsManager SmsManager smsManager = SmsManager.getDefault(); // Send a text based SMS smsManager.sendTextMessage(phoneNumber, null, smsBody, sentPendingIntent, deliveredPendingIntent);
The code is pretty simple actually if you know the concept of pending intents and broadcast receivers.
Divide and Send Multipart Text Messages
Generally an SMS is restricted to 160 (7 bit) characters or 140 (8 bit) characters. So if your message is longer than that, then you’ll have to divide it into multiple parts using divideMessage()
and then send with the sendMultipartTextMessage()
method. It’s fairly simple, let’s see an example.
// Get the default instance of SmsManager SmsManager smsManager = SmsManager.getDefault(); String phoneNumber = "9999999999"; String smsBody = "Some piece of really long text, longer than 140*n characters!"; String SMS_SENT = "SMS_SENT"; String SMS_DELIVERED = "SMS_DELIVERED"; PendingIntent sentPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0); PendingIntent deliveredPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_DELIVERED), 0); ArrayList<String> smsBodyParts = smsManager.divideMessage(smsBody); ArrayList<PendingIntent> sentPendingIntents = new ArrayList<PendingIntent>(); ArrayList<PendingIntent> deliveredPendingIntents = new ArrayList<PendingIntent>(); for (int i = 0; i < smsBodyParts.size(); i++) { sentPendingIntents.add(sentPendingIntent); deliveredPendingIntents.add(deliveredPendingIntent); } // For when the SMS has been sent registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(context, "SMS sent successfully", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(context, "Generic failure cause", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NO_SERVICE: Toast.makeText(context, "Service is currently unavailable", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(context, "No pdu provided", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(context, "Radio was explicitly turned off", Toast.LENGTH_SHORT).show(); break; } } }, new IntentFilter(SMS_SENT)); // For when the SMS has been delivered registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS delivered", Toast.LENGTH_SHORT).show(); break; case Activity.RESULT_CANCELED: Toast.makeText(getBaseContext(), "SMS not delivered", Toast.LENGTH_SHORT).show(); break; } } }, new IntentFilter(SMS_DELIVERED)); // Send a text based SMS smsManager.sendMultipartTextMessage(phoneNumber, null, smsBodyParts, sentPendingIntents, deliveredPendingIntents);
Send Binary (Data) SMS
We can send binary messages (as opposed to text based messages that we covered earlier) to specific application ports using sendDataMessage()
. According to this Stack Exchange thread data sms is one which is sent over 2G/3G as well as GSM. I’ve tested it with mobile data turned off and it works fine charging me the same amount, so not very sure on whether it uses 2G/3G or not, but generally the term data is used in telephony when it’s related to network (tcp/ip). Anyway, SMS’s are generally sent to a specific port on the device (which is probably port 0 [zero]). But using sendDataMessage()
we can send SMS’s to some other random port on which our app can listen for incoming SMSs and do something with that. In this case the default messaging app will not store the SMSs in their inbox for both the sender as well as the receiver.
For binary messages, Java uses UTF-8 encoding hence the length is 140 characters out of which the User Data Header (UDH) data size is 7 bytes, hence 133 characters can be sent. More details on SMPP, PDUs and UDH here.
We’ll see how to send data messages now.
// Get the default instance of SmsManager SmsManager smsManager = SmsManager.getDefault(); String phoneNumber = "9999999999"; byte[] smsBody = "Let me know if you get this SMS".getBytes(); short port = 6734; // Send a text based SMS smsManager.sendDataMessage(phoneNumber, null, port, smsBody, null, null);
This piece of code will send a data message to the port 2345
on the recipient’s device.
Receive SMS Text Messages
Now that we know how to send SMS messages, it’s time to see how to receive them using broadcast receivers as soon as the recipient device receives it.
Firstly, you’ll need the RECEIVE_SMS
permission, so put this in your manifest:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
Next we’ll create a broadcast receiver class called SmsManager.java
to listen for any incoming SMS. So create the file and put this code into it:
public class SmsReceiver extends BroadcastReceiver { private String TAG = SmsReceiver.class.getSimpleName(); public SmsReceiver() { } @Override public void onReceive(Context context, Intent intent) { // Get the data (SMS data) bound to intent Bundle bundle = intent.getExtras(); SmsMessage[] msgs = null; String str = ""; if (bundle != null) { // Retrieve the SMS Messages received Object[] pdus = (Object[]) bundle.get("pdus"); msgs = new SmsMessage[pdus.length]; // For every SMS message received for (int i=0; i < msgs.length; i++) { // Convert Object array msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); // Sender's phone number str += "SMS from " + msgs[i].getOriginatingAddress() + " : "; // Fetch the text message str += msgs[i].getMessageBody().toString(); // Newline :-) str += "\n"; } // Display the entire SMS Message Log.d(TAG, str); } } }
It’ll loop through all the PDUs (one for each message in a multipart text message) and form a big string with the sender phone number and the messages. Finally it logs the entire string formed. This reception technique will work for both single and multipart messages. You’ll also need create an entry for the receiver in the manifest file with an SMS_RECEIVED_ACTION
intent filter.
<receiver android:name=".SmsReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="999"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
You can learn more about intent filter android:priority
here.
The code will dump something like this:
D/SmsReceiver(21819): SMS from +919999999999 : This is an SMS!
Binary SMS Receiver
When a binary SMS is received, the code has to be a little different as the data received is not in plain text but binary. Here’s the code handling the reception:
public class SmsReceiver extends BroadcastReceiver { private String TAG = SmsReceiver.class.getSimpleName(); public SmsReceiver() { } @Override public void onReceive(Context context, Intent intent) { // Get the data (SMS data) bound to intent Bundle bundle = intent.getExtras(); SmsMessage[] msgs = null; String str = ""; if (bundle != null){ // Retrieve the Binary SMS data Object[] pdus = (Object[]) bundle.get("pdus"); msgs = new SmsMessage[pdus.length]; // For every SMS message received (although multipart is not supported with binary) for (int i=0; i<msgs.length; i++) { byte[] data = null; msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); str += "Binary SMS from " + msgs[i].getOriginatingAddress() + " :"; str += "\nBINARY MESSAGE: "; // Return the User Data section minus the // User Data Header (UDH) (if there is any UDH at all) data = msgs[i].getUserData(); // Generally you can do away with this for loop // You'll just need the next for loop for (int index=0; index < data.length; index++) { str += Byte.toString(data[index]); } str += "\nTEXT MESSAGE (FROM BINARY): "; for (int index=0; index < data.length; index++) { str += Character.toString((char) data[index]); } str += "\n"; } // Dump the entire message // Toast.makeText(context, str, Toast.LENGTH_LONG).show(); Log.d(TAG, str); } } }
It’ll dump something like this:
D/SmsReceiver(25507): Binary SMS from +919999999999 : D/SmsReceiver(25507): BINARY MESSAGE: 7610111632109101321071101111193210510232121111117321031011163211610410511532837783 D/SmsReceiver(25507): TEXT MESSAGE (FROM BINARY): Let me know if you get this SMS
The entry in the manifest file will also be a little different with the most important portion beind, specifying the port and a different action (from the previous one).
<receiver android:name=".SmsReceiver" android:enabled="true" android:exported="true" > <intent-filter android:priority="10" > <action android:name="android.intent.action.DATA_SMS_RECEIVED" /> <data android:scheme="sms" android:host="*" android:port="6734" /> </intent-filter> </receiver>
You should read this SO thread that has some useful information regarding the manifest entry.
Dynamic BroadcastReceiver Registration
We registered our broadcast receiver in the manifest file (statically). Alternatively, we can also dynamically register it in the code itself. You should completely understand the difference between statically and dynamically registered broadcast receivers. I’ve explained everything in my article. Let’s see how to register the previous binary sms receiver example dynamically in code:
// Register a broadcast receiver IntentFilter intentFilter = new IntentFilter("android.intent.action.DATA_SMS_RECEIVED"); intentFilter.setPriority(10); intentFilter.addDataScheme("sms"); intentFilter.addDataAuthority("*", "6734"); registerReceiver(smsReceiver, intentFilter);
The object smsReceiver
is that of the SmsReceiver
broadcast receiver implementation (class). Instead of having a separate class you can also have smsReceiver
as an instance member variable to which you assign an anonymous class implementation of BroadcastReceiver
.
BroadcastReceiver smsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // code to read the incoming SMS } }
Changes in SMS Reception Since Android 4.4 KitKat (API Level 19)
A lot has changed in Android 4.4 (API level 19). Firstly, with just SMS_RECEIVED_ACTION
intent filter, apps can only “observe” (or “monitor”) incoming messages, i.e., read them for purposes like phone verification. But this will not give you write access to the SMS provider (content provider) defined by the android.provider.Telephony
class and subclasses. This also means now you cannot delete messages.
To get write access to the provider, you’ll need your app to be selected as the default messaging app by the user. Then incoming SMS’s will be delivered to your app with the SMS_DELIVER_ACTION
intent and also you’ll need to request the BROADCAST_SMS
permission.
You can find more information on the process to build a full blown messaging app that works on Kitkat and above here.
Pre-Android 4.4, the SMS_RECEIVED_ACTION
intents were ordered broadcasts that could be aborted by one of the receivers. This would cause a lot of issues in some cases where a third party app with highest priority would abort the broadcast preventing it from getting received by another third party app’s receivers. Since Kitkat, this is not possible anymore (good news!), i.e., any attempt to abort the broadcast will be ignored and all the apps who had registered to receive incoming messages will get a chance.
Wrapping Up
So we’ve learnt all the different ways in which one can send and receive messages from an Android application programatically. Although receiving is not something that would work consistently across all heavily used versions (as of now), because of the conceptual (and implementation) changes since Android 4.4. If you’re building an application that will require write access to the SMS Content Provider as it might let the user perform all sorts of tasks related to messaging, then you should definitely read this article to make your SMS app Kitkat (and above) ready. Do note that the SmsManager
API can also be used to send MMS (Multimedia Messagin Service) messages.
Hi rishabh ,
I am developing an application in which i am able to fetch the contacts list from android phone in the autocomplete textview and then on save button that 5 different phone numbers are saved in the database…now what i am doing is using sqlite database and able to fetch the phone numbers from it. but now i want when anyone click on send sms button the message should be sent to these 5 phone numbers at once using smsmanager class. However i am not able to do it.Can you help me on it..Thanks.