Sending and Receiving SMS and MMS in Android (pre Kit Kat Android 4.4)
Asked Answered
T

6

134

I have figured out how to send and receive SMS messages. To send SMS messages I had to call the sendTextMessage() and sendMultipartTextMessage() methods of the SmsManager class. To receive SMS messages, I had to register a receiver in the AndroidMainfest.xml file. Then I had to override the onReceive() method of the BroadcastReceiver. I have included examples below.

MainActivity.java

public class MainActivity extends Activity {
    private static String SENT = "SMS_SENT";
    private static String DELIVERED = "SMS_DELIVERED";
    private static int MAX_SMS_MESSAGE_LENGTH = 160;

    // ---sends an SMS message to another device---
    public static void sendSMS(String phoneNumber, String message) {

        PendingIntent piSent = PendingIntent.getBroadcast(mContext, 0, new Intent(SENT), 0);
        PendingIntent piDelivered = PendingIntent.getBroadcast(mContext, 0,new Intent(DELIVERED), 0);
        SmsManager smsManager = SmsManager.getDefault();

        int length = message.length();          
        if(length > MAX_SMS_MESSAGE_LENGTH) {
            ArrayList<String> messagelist = smsManager.divideMessage(message);          
            smsManager.sendMultipartTextMessage(phoneNumber, null, messagelist, null, null);
        }
        else
            smsManager.sendTextMessage(phoneNumber, null, message, piSent, piDelivered);
        }
    }

    //More methods of MainActivity ...
}

SMSReceiver.java

public class SMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
    private Context mContext;
    private Intent mIntent;

    // Retrieve SMS
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        mIntent = intent;

        String action = intent.getAction();

        if(action.equals(ACTION_SMS_RECEIVED)){

            String address, str = "";
            int contactId = -1;

            SmsMessage[] msgs = getMessagesFromIntent(mIntent);
            if (msgs != null) {
                for (int i = 0; i < msgs.length; i++) {
                    address = msgs[i].getOriginatingAddress();
                    contactId = ContactsUtils.getContactId(mContext, address, "address");
                    str += msgs[i].getMessageBody().toString();
                    str += "\n";
                }
            }   

            if(contactId != -1){
                showNotification(contactId, str);
            }

            // ---send a broadcast intent to update the SMS received in the
            // activity---
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("SMS_RECEIVED_ACTION");
            broadcastIntent.putExtra("sms", str);
            context.sendBroadcast(broadcastIntent);
        }

    }

    public static SmsMessage[] getMessagesFromIntent(Intent intent) {
        Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
        byte[][] pduObjs = new byte[messages.length][];

        for (int i = 0; i < messages.length; i++) {
            pduObjs[i] = (byte[]) messages[i];
        }
        byte[][] pdus = new byte[pduObjs.length][];
        int pduCount = pdus.length;
        SmsMessage[] msgs = new SmsMessage[pduCount];
        for (int i = 0; i < pduCount; i++) {
            pdus[i] = pduObjs[i];
            msgs[i] = SmsMessage.createFromPdu(pdus[i]);
        }
        return msgs;
    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_MMS" />
    <uses-permission android:name="android.permission.WRITE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher_icon"
        android:label="@string/app_name" >

        <activity
            //Main activity...
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            //Activity 2 ...
        </activity>
        //More acitivies ...

        // SMS Receiver
        <receiver android:name="com.myexample.receivers.SMSReceiver" >
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

    </application>
</manifest>

However, I was wondering if you could send and receive MMS messages in a similar fashion. After doing some research, many examples provided on blogs simply pass an Intent to the native Messaging application. I am trying to send an MMS without leaving my application. There doesn't seem to be a standard way of sending and receiving MMS. Has anyone gotten this to work?

Also, I am aware that the SMS/MMS ContentProvider is not a part of the official Android SDK, but I was thinking someone may have been able to implement this. Any help is greatly appreciated.

Update

I have added a BroadcastReceiver to the AndroidManifest.xml file to receive MMS messages

<receiver android:name="com.sendit.receivers.MMSReceiver" >
    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />

        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>
</receiver>

In the MMSReceiver class, the onReceive() method is only able to grab the phoneNumber that the message was sent from. How do you grab other important things from an MMS such as the file path to the media attachment (image/audio/video), or the text in the MMS?

MMSReceiver.java

public class MMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_MMS_RECEIVED = "android.provider.Telephony.WAP_PUSH_RECEIVED";
    private static final String MMS_DATA_TYPE = "application/vnd.wap.mms-message";

     // Retrieve MMS
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        String type = intent.getType();

        if(action.equals(ACTION_MMS_RECEIVED) && type.equals(MMS_DATA_TYPE)){

            Bundle bundle = intent.getExtras();

            Log.d(DEBUG_TAG, "bundle " + bundle);
            SmsMessage[] msgs = null;
            String str = "";
            int contactId = -1;
            String address;

            if (bundle != null) {

                byte[] buffer = bundle.getByteArray("data");
                Log.d(DEBUG_TAG, "buffer " + buffer);
                String incomingNumber = new String(buffer);
                int indx = incomingNumber.indexOf("/TYPE");
                if(indx>0 && (indx-15)>0){
                    int newIndx = indx - 15;
                    incomingNumber = incomingNumber.substring(newIndx, indx);
                    indx = incomingNumber.indexOf("+");
                    if(indx>0){
                        incomingNumber = incomingNumber.substring(indx);
                        Log.d(DEBUG_TAG, "Mobile Number: " + incomingNumber);
                    }
                }

                int transactionId = bundle.getInt("transactionId");
                Log.d(DEBUG_TAG, "transactionId " + transactionId);

                int pduType = bundle.getInt("pduType");
                Log.d(DEBUG_TAG, "pduType " + pduType);

                byte[] buffer2 = bundle.getByteArray("header");      
                String header = new String(buffer2);
                Log.d(DEBUG_TAG, "header " + header);

                if(contactId != -1){
                    showNotification(contactId, str);
                }

                // ---send a broadcast intent to update the MMS received in the
                // activity---
                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction("MMS_RECEIVED_ACTION");
                broadcastIntent.putExtra("mms", str);
                context.sendBroadcast(broadcastIntent);

            }
        }

    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

According to the Documentation of android.provider.Telephony:

Broadcast Action: A new text based SMS message has been received by the device. The intent will have the following extra values:

pdus - An Object[] of byte[]s containing the PDUs that make up the message.

The extra values can be extracted using getMessagesFromIntent(android.content.Intent) If a BroadcastReceiver encounters an error while processing this intent it should set the result code appropriately.

 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
 public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";

Broadcast Action: A new data based SMS message has been received by the device. The intent will have the following extra values:

pdus - An Object[] of byte[]s containing the PDUs that make up the message.

The extra values can be extracted using getMessagesFromIntent(android.content.Intent). If a BroadcastReceiver encounters an error while processing this intent it should set the result code appropriately.

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";

Broadcast Action: A new WAP PUSH message has been received by the device. The intent will have the following extra values:

transactionId (Integer) - The WAP transaction ID

pduType (Integer) - The WAP PDU type`

header (byte[]) - The header of the message

data (byte[]) - The data payload of the message

contentTypeParameters (HashMap<String,String>) - Any parameters associated with the content type (decoded from the WSP Content-Type header)

If a BroadcastReceiver encounters an error while processing this intent it should set the result code appropriately. The contentTypeParameters extra value is map of content parameters keyed by their names. If any unassigned well-known parameters are encountered, the key of the map will be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter. If a parameter has No-Value the value in the map will be null.

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";

Update #2

I have figured out how to pass extras in a PendingIntent to be received by a BroadcastReceiver: Android PendingIntent extras, not received by BroadcastReceiver

However, the extra gets passed to the SendBroadcastReceiver not the SMSReceiver. How can I pass an extra to the SMSReceiver?

Update #3

Receiving MMS

So after doing more research I saw some suggestions of registering a ContentObserver. That way you can detect when there are any changes to the content://mms-sms/conversations Content Provider, consequently allowing you to detect incoming MMS. Here is the closest example to get this to work that I have found: Receiving MMS

However, there is a variable mainActivity of type ServiceController. Where is the ServiceController class implemented? Are there any other implementations of a registered ContentObserver?

Sending MMS

As for sending MMS, I have come across this example: Send MMS

The problem is that I tried running this code on my Nexus 4, which is on Android v4.2.2, and I am receiving this error:

java.lang.SecurityException: No permission to write APN settings: Neither user 10099 nor current process has android.permission.WRITE_APN_SETTINGS.

The error gets thrown after querying the Carriers ContentProvider in the getMMSApns() method of the APNHelper class.

final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), null, null, null, null);

Apparently you can't read APNs in Android 4.2

What is the alternative for all those applications which use mobile data to perform operations (like sending MMS) and don't know the default APN setting present in the device?

Update #4

Sending MMS

I have tried following this example: Send MMS

As @Sam suggested in his answer:

You have to add jsoup to the build path, the jar to the build path and import com.droidprism.*; To do that in android, add the jars to the libs directory first, then configure the project build path to use the jars already in the libs directory, then on the build path config click order and export and check the boxes of the jars and move jsoup and droidprism jar to the top of the build order.

So now I no longer get the SecurityException errors. I am testing now on a Nexus 5 on Android KitKat. After running the sample code it gives me a 200 response code after the call to

MMResponse mmResponse = sender.send(out, isProxySet, MMSProxy, MMSPort);

However, I checked with the person I tried sending the MMS to. And they said they never received the MMS.

Thurston answered 22/1, 2013 at 6:29 Comment(9)
Have you taken a look at this tutorial before? maximbogatov.wordpress.com/2011/08/13/mms-in-androidAmenity
Yes I have. I tried piecing Maxim's answer together but am not able to get it to work. There are many classes in there that import android.provider.telephony which seems to be deprecated.Tarahtaran
And presumably, after reading @Sahil's answer, you've also tried this: #2973345Amenity
I'm not sure how to piece that answer together, although it looks very similar to @Sahil's answer.Tarahtaran
Hi @Thurston are you able to find the solution for all those query you mention above.. ?Madid
Hey @kamal_tech_view, I still am looking for an answer to this problem. I have posted several updates of progress I have made many attempts at certain code examples but nothing has been successful. Do you have any ideas? I will accept an answer if in fact it addresses sending and receiving MMS.Tarahtaran
regarding reading the incoming mms I find the better approach is to read the mms content with url but I am unable to download the mms content you can find this post here #16060673Madid
I've tried the ContentObserver method, and the BroadcastReceiver method for receiving MMS messages and with either approach the problem is that my app receives the MMS-received notification way before the built-in text app shows a message. So my app sounds the alarm, then the user goes to look in their texting app, and the image of the new message isn't showing yet. About 3 minutes later the texting app will have the image visible. How to prevent my app from sounding the alarm too soon ?Especially
Looks like there may be some changes ahead of us : android-developers.blogspot.com/2013/10/…Tarahtaran
M
15

I had the exact same problem you describe above (Galaxy Nexus on t-mobile USA) it is because mobile data is turned off.

In Jelly Bean it is: Settings > Data Usage > mobile data

Note that I have to have mobile data turned on PRIOR to sending an MMS OR receiving one. If I receive an MMS with mobile data turned off, I will get the notification of a new message and I will receive the message with a download button. But if I do not have mobile data on prior, the incoming MMS attachment will not be received. Even if I turn it on after the message was received.

For some reason when your phone provider enables you with the ability to send and receive MMS you must have the Mobile Data enabled, even if you are using Wifi, if the Mobile Data is enabled you will be able to receive and send MMS, even if Wifi is showing as your internet on your device.

It is a real pain, as if you do not have it on, the message can hang a lot, even when turning on Mobile Data, and might require a reboot of the device.

Macro answered 14/2, 2013 at 13:23 Comment(3)
Also you must know that sending a SMS and MMS are 2 entirely different things in background. MMS is more of an internet based network service as it requires sending additional items(Media) with text. The given code works fine on a few devices that i've tested on. ps : you can ignore the NOKIA part.Macro
When i run this example, in LogCat it prints: 02-24 13:32:40.872: V/SendMMSActivity(5686): TYPE_MOBILE_MMS not connected, bail 02-24 13:32:40.882: V/SendMMSActivity(5686): type is not TYPE_MOBILE_MMS, bail It also says: java.lang.SecurityException: No permission to write APN settings: Neither user 10099 nor current process has android.permission.WRITE_APN_SETTINGS. It looks like it can't execute this query: final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), null, null, null, null); Im testing on a Nexus 4.Tarahtaran
Also this is the same example that @Sahil provided.Tarahtaran
D
7

There is not official api support which means that it is not documented for the public and the libraries may change at any time. I realize you don't want to leave the application but here's how you do it with an intent for anyone else wondering.

public void sendData(int num){
    String fileString = "..."; //put the location of the file here
    Intent mmsIntent = new Intent(Intent.ACTION_SEND);
    mmsIntent.putExtra("sms_body", "text");
    mmsIntent.putExtra("address", num);
    mmsIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileString)));
    mmsIntent.setType("image/jpeg");
    startActivity(Intent.createChooser(mmsIntent, "Send"));

}

I haven't completely figured out how to do things like track the delivery of the message but this should get it sent.

You can be alerted to the receipt of mms the same way as sms. The intent filter on the receiver should look like this.

<intent-filter>
    <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
    <data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
Driblet answered 23/1, 2013 at 18:17 Comment(3)
Doesn't this just launch the native Messaging app?Tarahtaran
yeah sorry about that. i just realized that you already knew how to do that. i did add how to receive mms though.Driblet
Thanks, I have recently been implementing part of the MMS BroadcastReceiver, and have used the Intent Filter that you have posted. I will update this question soon.Tarahtaran
C
4

To send an mms for Android 4.0 api 14 or higher without permission to write apn settings, you can use this library: Retrieve mnc and mcc codes from android, then call

Carrier c = Carrier.getCarrier(mcc, mnc);
if (c != null) {
    APN a = c.getAPN();
    if (a != null) {
        String mmsc = a.mmsc;
        String mmsproxy = a.proxy; //"" if none
        int mmsport = a.port; //0 if none
    }
}

To use this, add Jsoup and droid prism jar to the build path, and import com.droidprism.*;

Caelian answered 22/1, 2013 at 6:29 Comment(3)
Hey @Sam, I added the .jar file to my project but am getting this error at the line that instantiates the Carrier object : java.lang.NoClassDefFoundError: com.droidprism.Carrier is that happening to you?Tarahtaran
no. You have to add jsoup to the build path, the jar to the build path and import com.droidprism.*; I'll edit the answer. To do that in android, add the jars to the libs directory first, then configure the project build path to use the jars already in the libs directory, then on the build path config click order and export and check the boxes of the jars and move jsoup and droidprism jar to the top of the build order.Jyoti
Adding the Jsoup .jar resolved the NoClassDefFoundError. I can now get the APN settings. The next step is to figure out how to send MMS.Tarahtaran
W
3

I dont think there is any sdk support for sending mms in android. Look here Atleast I havent found yet. But a guy claimed to have it. Have a look at this post.

Send MMS from My application in android

Wether answered 22/1, 2013 at 6:46 Comment(2)
I looked at the comments of the androidbridge.blogspot.com post of the Nokia implementation, and it looks like many people are having issues getting this to work on their devices.Tarahtaran
@Thurston So, there might be no support for it yet.Wether
L
-2

I dont understand the frustrations. Why not just make a broadcastreceiver that filters for this intent:

android.provider.Telephony.MMS_RECEIVED

I checked a little further and you might need system level access to get this (rooted phone).

Lalittah answered 22/1, 2013 at 6:29 Comment(3)
Hey @j2emanue, The problem is after you receive this intent, how do you actually get the content of the MMS? If an MMS contains and image and text, how do you extract these components.Tarahtaran
but i notice theres a byte array extra you can get if you do it the way .i mentioned .... byte[] data = intent.getByteArrayExtra("data"); im not sure how to parse it though sorry.Lalittah
i am able to parse it. but all i can get is the subject, who the mms came from, and the contentlocation where the content of the mms is stored. however this url is not accessible.Tarahtaran
P
-3

SmsListenerClass

public class SmsListener extends BroadcastReceiver {

static final String ACTION =
        "android.provider.Telephony.SMS_RECEIVED";

@Override
public void onReceive(Context context, Intent intent) {

    Log.e("RECEIVED", ":-:-" + "SMS_ARRIVED");

    // TODO Auto-generated method stub
    if (intent.getAction().equals(ACTION)) {

        Log.e("RECEIVED", ":-" + "SMS_ARRIVED");

        StringBuilder buf = new StringBuilder();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {

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

            SmsMessage[] messages = new SmsMessage[pdus.length];
            SmsMessage message = null;

            for (int i = 0; i < messages.length; i++) {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    String format = bundle.getString("format");
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
                } else {
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                }

                message = messages[i];
                buf.append("Received SMS from  ");
                buf.append(message.getDisplayOriginatingAddress());
                buf.append(" - ");
                buf.append(message.getDisplayMessageBody());
            }

            MainActivity inst = MainActivity.instance();
            inst.updateList(message.getDisplayOriginatingAddress(),message.getDisplayMessageBody());

        }

        Log.e("RECEIVED:", ":" + buf.toString());

        Toast.makeText(context, "RECEIVED SMS FROM :" + buf.toString(), Toast.LENGTH_LONG).show();

    }
}

Activity

@Override
public void onStart() {
    super.onStart();
    inst = this;
}

public static MainActivity instance() {
    return inst;
}

public void updateList(final String msg_from, String msg_body) {

    tvMessage.setText(msg_from + " :- " + msg_body);

    sendSMSMessage(msg_from, msg_body);

}

protected void sendSMSMessage(String phoneNo, String message) {

    try {
        SmsManager smsManager = SmsManager.getDefault();
        smsManager.sendTextMessage(phoneNo, null, message, null, null);
        Toast.makeText(getApplicationContext(), "SMS sent.", Toast.LENGTH_LONG).show();
    } catch (Exception e) {
        Toast.makeText(getApplicationContext(), "SMS faild, please try again.", Toast.LENGTH_LONG).show();
        e.printStackTrace();
    }
}

Manifest

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

<receiver android:name=".SmsListener">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
Pyelitis answered 22/1, 2013 at 6:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.