MMS in Android. Part 1. Sending and receiving MMS

August 13, 2011 § 5 Comments

Judging by forums questions (and my own experience) sending mms programmatically is quite complicate issue. That is because there is no documented API for it. To start working with mms we will need to use some undocumented classes. Happily Android OS is open source project. So we can download some classes from http://android.git.kernel.org/ and use it in our code. I know that it is not the best solution (while in future version this code could be modified) but it is the only one if you want to work with mms from your code.

Before starting working with mms I would recommend get to touch with Multimedia Messaging Service Encapsulation Protocol documentation.

There are different Multimedia Message Types. Each of them has own purpose. Here is the most important ones that we will use below:

  • “m-send-req” – sending message
  • “m-notification-ind” – provide the MMS Client with information about a MM located at the recipient MMS Proxy-Relay and waiting for retrieval. Client after that should fetch MM. It doesn’t contain Message Body but only Header.
  • “m-delivery-ind” – delivery report of MM

Here are inner classes that we can use.

We will need (you can copy the whole file or “borrow part of them”):

  • android.provider.Telephony
  • com.google.android.mms.ContentType
Files from com.google.android.mms.pdu package:
  • PduHeaders – contains map of headers
  • PduBody – includes PduParts
  • PduPart – consists of part header, data uri, part data
  • GenericPdu – wraps PduHeader
  • MultimediaMessagePdu – extends GenericPdu, adds PduBody to it
  • SendReq – extends MultimediaMessagePdu, represents sending request – “m-send-req” type
  • DeliveryInd – extends GenericPdu , represents delivery report – “m-delivery-ind” type
  • NotificationInd –  extends GenericPdu , represents notification about incoming message (that is stored in MMBOX and should be retrieved from there) – “m-notification-ind” type
  • PduComposer – translate Pdu to byte[]
  • PduParser – translate byte[] to Pdu
  • PduPersister – stores MM in storage.

Ok, here are the tasks to implement comprehencive mms support for your application:

  • Sending mms
  • Receiving delivery/read report
  • Receiving incoming mms
  • Getting list of mms from storage
  • Inserting/updating/deleting mms data to/at/from storage

First of all you should understand what points from above list you are going to implement.

So, in order…

Sending mms

MMS unlike SMS is send through http connection. Actually it is just request to mmsc (Multimedia Message System Center).

First of all we have to request Connectivity Service to enable mms:

  ConnectivityManager mConnMgr = 
        (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
  int result = mConnMgr.startUsingNetworkFeature(
        ConnectivityManager.TYPE_MOBILE, "enableMMS");

If result is 0 (APN_ALREADY_ACTIVE) we can start sending MMS. But likely it is not. In that case we have to wait while it become ACTIVE. We can register broadcast receiver that listens “ConnectivityManager.CONNECTIVITY_ACTION” action. At onReceive() method of our receiver we check whether connectivity manager is ready to send mms:

    String action = intent.getAction();
    if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
        return;
    }

    NetworkInfo mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
        ConnectivityManager.EXTRA_NETWORK_INFO);

    // Check availability of the mobile network.
    if ((mNetworkInfo == null) ||
        (mNetworkInfo.getType() != ConnectivityManager.TYPE_MOBILE_MMS)) {
        return;
    }

    if (!mNetworkInfo.isConnected()) {
        return;
    } else {
	//send mms
    }

How to send mms when we are ready to do it? We can watch sources of standard messaging client that are open as almost everything in Android. To implement the same solution is not too complicated but quite intricate.

Or we can use API that was designed by Nokia that encapsulates all complexity. You can find more information and links here.

But none of the solutions works with Samsung (at list Galaxy S and Galaxy Tab). It throws exception during http request to mmsc. It seems that Samsung have changed some network properties. I have not yet figured out how to cope with it.

Receiving delivery/read report

We should implement BroadcastReceiver with follow intent filter:

    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>

In onReceive() we get pdu byte[] from intent and transform it in GenericPdu object via PduParser:

    byte[] pushData = intent.getByteArrayExtra("data");
    PduParser parser = new PduParser(pushData);
    GenericPdu pdu = parser.parse();

Than we get the pdu’s type and check whether it delivery/read report:

    int type = pdu.getMessageType();
    if(type == PduHeaders.MESSAGE_TYPE_DELIVERY_IND) {
        String messageId = new String(((DeliveryInd)pdu).getMessageId());
        //Notify app that mms with messageId was delivered
    }

Ok, here we can make a try to update data in storage. But it is not necessary. Standard messenger will do it for us. While device is not rooted it is impossible to delete or shut down standard client. And it makes our way a bit easier.

But if we want to make it anyway we get delivery status and make appropriate update of storage:

    int status = ((DeliveryInd)pdu).getStatus();
    if(status == PduHeaders.STATUS_RETRIEVED) {
        //message delivered. update storage
    }

There could be follow statuses:

  • PduHeaders.STATUS_EXPIRED
  • PduHeaders.STATUS_RETRIEVED
  • PduHeaders.STATUS_REJECTED
  • PduHeaders.STATUS_DEFERRED
  • PduHeaders.STATUS_UNRECOGNIZED
  • PduHeaders.STATUS_INDETERMINATE
  • PduHeaders.STATUS_FORWARDED
  • PduHeaders.STATUS_UNREACHABLE
The meaning is clear from it name. We can define how to act in each case. For example that’s how we can save update if the status ==  STATUS_RETRIEVED (delivered):
    ContentValues values = new ContentValues();
    values.put("msg_box; //type 2 == MESSAGE_TYPE_SENT
    String where = "_id" + " = '" + messageId + "'";
    context.getContentResolver().update(
            Uri.parse("content://mmsalues, where, null);

Receiving incoming MMS

When we have new inbox MMS we receive (in our broadcast receiver that is described above) pdu that has “m-notification-ind” type. But this pdu doesn’t contain multimedia data but only location at MMBOX of our mmsc where the content is situated. And we have to download it from that location.

Actually standard message client will do it for us. I will not write how to do it by our own. It is complicated process that take a lot of code at dozen classes. You can follow the path of tasks starting from com.android.mms.transaction.PduReceiver onReceive() method. If you really need the solution you can always use the code from that package.

출처 : https://maximbogatov.wordpress.com/2011/08/13/mms-in-android/

 

 

 

 

 

 

MMS는 좀 어려운데, 검색하면 PduParser 등을 이용해야 하는 걸로 많이 나오는데, 딸린 파일이 엄청 많다.

안드로이드 기본 소스에는 들어가있는 것처럼 나오던데, 이상하게 저 클래스를 쓸 수가 없더라.

그렇다고 17MB짜리 jar를 추가해서 쓰기도 좀 그렇고….

 

*LG G2에서는 MMS의 발신자 정보 들어있는 곳에 수신자 전화번호가 보여서 가져올 수가 없다.

출처 : http://susemi99.kr/664

Uri uri = Uri.parse("content://mms/");
String selection = "_id = " + mmsId;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
 
 
 
String selectionPart = "mid=" + mmsId;
Android.Net.Uri uri = Android.Net.Uri.parse("content://mms/part");
Android.Database.ICursor cursor = ContentResolver.Query(uri, null,
    selectionPart, null, null);
if (cursor.MoveToFirst())
{
    do
    {
        String partId = cursor.GetString(cursor.GetColumnIndex("_id"));
        String type = cursor.GetString(cursor.GetColumnIndex("ct"));
        if ("text/plain".Equals(type))
        {
            String data = cursor.GetString(cursor.GetColumnIndex("_data"));
            String body;
            if (data != null)
            {
                // implementation of this method below
                body = GetMmsText(partId);
            }
            else
            {
                body = cursor.GetString(cursor.GetColumnIndex("text"));
            }
        }
    } while (cursor.MoveToNext());
}

private string getMmsText(String id)
{
    Android.Net.Uri partURI = Android.Net.Uri.Parse("content://mms/part/" + id);
    Stream inputStream = null;
    Java.Lang.StringBuilder sb = new Java.Lang.StringBuilder();
    try
    {
        inputStream = ContentResolver.OpenInputStream(partURI);
        if (inputStream != null)
        {
            Java.IO.InputStreamReader isr = new Java.IO.InputStreamReader(inputStream, "UTF-8");
            Java.IO.BufferedReader reader = new Java.IO.BufferedReader(isr);
            string temp = reader.ReadLine();
            while (temp != null)
            {
                sb.Append(temp);
                temp = reader.ReadLine();
            }
        }
    }
    catch (IOException e) { }
    finally
    {
        if (inputStream != null)
        {
            try
            {
                inputStream.Close();
            }
            catch (IOException e) { }
        }
    }
    return sb.ToString();
}
 

 

+ Recent posts

티스토리 툴바