1.彩信接收步骤
我们首先看4个类分别是PDU重要的几个类
PduPersister 用于管理PDU存储
PduParser 用于解析PDU
PduComposer 用于生成PDU
关键的方法:
PduPersister 类
PduPersister getPduPersister(Context) Get the object
Uri persist(GenericPdu, Uri) 把一个GenericPdu保存到Uri所指定的数据库中,返回指向新生成数据的Uri ,获取的数据也 可以保存到数据库中
GenericPdu load(Uri) 从数据库把Uri所指的数据加载出来成一个GenericPdu对象
Uri move(Uri, Uri) 把Pdu从一个地方移到另一个地方,比如从草稿箱移动到发件箱,当MMS已发送时。
为什么会要把PDU的存储也封装成PduPersister呢?因为PDU的存储方式 是放在标准的SQLiteDatabase中,通过TelephonyProvider,而SQLiteDatabase中存储不能以直接的PDU的字节流来存储,必须要把PDU拆解成为可读的字段,因此在存储PDU和从存储加载PDU的过程 中涉及到PDU数据上面的处理,因此封装出来,更方便使用。
PduParser:用于把PDU字节流解析成为Android可识别的GenericPdu
PduParser PduParser(byte[]) Construct an object
GenericPdu parse() Parse the PDU byte stream into Android PDU GenericPdu
PduComposer:把GenericPdu打包生成PDU字节流
Return Method Description
PduComposer PduComposer(Context, GenericPdu) Construct an object
byte[] make() Transfer the GenericPdu into a PDU byte stream
开始看代码
首先收到彩信是底层发送一个广播告诉上层的Mms,
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
PushReceiver.java 这个是一个广播
@Overridepublic void onReceive(Context context, Intent intent) {if (!MessageUtils.hasBasicPermissions()){Log.d(TAG, "PushReceiver do not have basic permissions");return;}mContext=context;if (intent.getAction().equals(WAP_PUSH_DELIVER_ACTION)&& (ContentType.MMS_MESSAGE.equals(intent.getType())|| WAP_PUSH_TYPE_SIC.equals(intent.getType())|| WAP_PUSH_TYPE_SLC.equals(intent.getType()))) {if (LOCAL_LOGV) {Log.v(TAG, "Received PUSH Intent: " + intent);}// Hold a wake lock for 5 seconds, enough to give any// services we start time to take their own wake locks.PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MMS PushReceiver");wl.acquire(5000);new ReceivePushTask(context).execute(intent); //开启异步进行下载}}
主要看收到彩信的一个Intent
收到彩信时 Intent
Intent { act=android.provider.Telephony.WAP_PUSH_DELIVER typ=application/vnd.wap.mms-message flg=0x18000010 cmp=com.android.mms/.transaction.PushReceiver(has extras)}
开启异步
private class ReceivePushTask extends AsyncTask<Intent,Void,Void> {private Context mContext;public ReceivePushTask(Context context) {mContext = context;}int hexCharToInt(char c) {if (c >= '0' && c <= '9') return (c - '0');if (c >= 'A' && c <= 'F') return (c - 'A' + 10);if (c >= 'a' && c <= 'f') return (c - 'a' + 10);}public String bytesToHexString(byte[] bytes) {if (bytes == null) return null;StringBuilder ret = new StringBuilder(2*bytes.length);for (int i = 0 ; i < bytes.length ; i++) {int b;b = 0x0f & (bytes[i] >> 4);ret.append("0123456789abcdef".charAt(b));b = 0x0f & bytes[i];ret.append("0123456789abcdef".charAt(b));}return ret.toString();}@Overrideprotected Void doInBackground(Intent... intents) {Intent intent = intents[0];// Get raw PDU push-data from the message and parse itbyte[] pushData = intent.getByteArrayExtra("data");if (DEBUG) {Log.d(TAG, "PushReceive: pushData= " + bytesToHexString(pushData));}SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);boolean wapPushEnabled = prefs.getBoolean(WAP_PUSH_MESSAGE, true);if (wapPushEnabled && (WAP_PUSH_TYPE_SIC.equals(intent.getType())|| WAP_PUSH_TYPE_SLC.equals(intent.getType()))) {ByteArrayInputStream bais = new ByteArrayInputStream(pushData);try {Class mWapPushHandler = Class.forName("com.qrd.wappush.WapPushHandler");Object WapPushHandlerObj = mWapPushHandler.newInstance();Method mHandleWapPush = mWapPushHandler.getDeclaredMethod("handleWapPush",InputStream.class, String.class, Context.class,int.class, String.class);Method mGetThreadID = mWapPushHandler.getDeclaredMethod("getThreadID");String address = intent.getStringExtra("address");if (address == null) {address = WAP_PUSH_DEFAULT_ADDRESS;}Uri pushMsgUri = (Uri)mHandleWapPush.invoke(WapPushHandlerObj, bais,intent.getType(), mContext,intent.getIntExtra(ConstantsWrapper.Phone.PHONE_KEY, 0),address + WAP_PUSH);if (pushMsgUri != null) {// Called off of the UI thread so ok to block.Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mContext.getApplicationContext(),(Long)mGetThreadID.invoke(WapPushHandlerObj));MessagingNotification.blockingUpdateNewMessageIndicator(mContext, (Long)mGetThreadID.invoke(WapPushHandlerObj), false);MmsWidgetProvider.notifyDatasetChanged(mContext);}} catch (Exception e) {Log.e(TAG, "Wap Push Hander Error :" + e);}return null;}PduParser parser = new PduParser(pushData,PduParserUtil.shouldParseContentDisposition());GenericPdu pdu = parser.parse();if (null == pdu) {Log.e(TAG, "Invalid PUSH data");return null;}PduPersister p = PduPersister.getPduPersister(mContext);ContentResolver cr = mContext.getContentResolver();int type = pdu.getMessageType();long threadId = -1;try {switch (type) {case MESSAGE_TYPE_DELIVERY_IND://143表示消息已送达,送达报告case MESSAGE_TYPE_READ_ORIG_IND: {threadId = findThreadId(mContext, pdu, type);if (threadId == -1) {// The associated SendReq isn't found, therefore skip// processing this PDU.break;}Message me = new Message();me.arg1=type;mHandler.sendMessage(me);//送达报告发送Uri uri = p.persist(pdu, Inbox.CONTENT_URI, true,MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);// Update thread ID for ReadOrigInd & DeliveryInd.ContentValues values = new ContentValues(1);values.put(Mms.THREAD_ID, threadId);SqliteWrapper.update(mContext, cr, uri, values, null, null);break;}case MESSAGE_TYPE_NOTIFICATION_IND: {//收到消息调用NotificationInd nInd = (NotificationInd) pdu;if (MmsConfig.getTransIdEnabled()) {byte [] contentLocation = nInd.getContentLocation();if ('=' == contentLocation[contentLocation.length - 1]) {//--byte [] transactionId = nInd.getTransactionId();byte [] contentLocationWithId = new byte [contentLocation.length+ transactionId.length];System.arraycopy(contentLocation, 0, contentLocationWithId,0, contentLocation.length);System.arraycopy(transactionId, 0, contentLocationWithId,contentLocation.length, transactionId.length);nInd.setContentLocation(contentLocationWithId);}}if (!isDuplicateNotification(mContext, nInd)) {//--int subId = intent.getIntExtra(ConstantsWrapper.Phone.SUBSCRIPTION_KEY, 0);//Phone ID will be updated in data baseLog.d(TAG, "PushReceiver subId : " + subId);ContentValues values = new ContentValues(1);values.put(Mms.SUBSCRIPTION_ID, subId);Uri uri = p.persist(pdu, Inbox.CONTENT_URI,true,MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext),null);SqliteWrapper.update(mContext, cr, uri, values, null, null);String address = pdu.getFrom().getString();threadId = MessagingNotification.getThreadId(mContext, uri);MessageUtils.markAsNotificationThreadIfNeed(mContext, threadId, address);if (!DownloadManager.getInstance().isAuto() &&!MessageUtils.isMobileDataEnabled(mContext, subId)) {MessagingNotification.blockingUpdateNewMessageIndicator(mContext,threadId, false);break;}Intent svc = new Intent(mContext, TransactionService.class);svc.putExtra(TransactionBundle.URI, uri.toString());svc.putExtra(TransactionBundle.TRANSACTION_TYPE,Transaction.NOTIFICATION_TRANSACTION);svc.putExtra(Mms.SUBSCRIPTION_ID, subId);mContext.startService(svc);} else if (LOCAL_LOGV) {Log.v(TAG, "Skip downloading duplicate message: "+ new String(nInd.getContentLocation()));}break;}default:Log.e(TAG, "Received unrecognized PDU.");}} catch (MmsException e) {Log.e(TAG, "Failed to save the data from PUSH: type=" + type, e);} catch (RuntimeException e) {Log.e(TAG, "Unexpected RuntimeException.", e);}if (LOCAL_LOGV) {Log.v(TAG, "PUSH Intent processed.");}return null;}@Overrideprotected void onProgressUpdate(Void... values) {super.onProgressUpdate(values);}}
主要看
Uri uri = p.persist(pdu, Inbox.CONTENT_URI,true,MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext),null);
p是一个PduPersister
p.persist();, 这里的操作是进行插入数据PDU表的数据 pdu里面自带一些协议
PduPersister.java
public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,HashMap<Uri, InputStream> preOpenedFiles)throws MmsException {if (uri == null) {throw new MmsException("Uri may not be null.");}long msgId = -1;try {msgId = ContentUris.parseId(uri);} catch (NumberFormatException e) {// the uri ends with "inbox" or something else like that}boolean existingUri = msgId != -1;if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {throw new MmsException("Bad destination, must be one of "+ "content://mms/inbox, content://mms/sent, "+ "content://mms/drafts, content://mms/outbox, "+ "content://mms/temp.");}synchronized(PDU_CACHE_INSTANCE) {if (PDU_CACHE_INSTANCE.isUpdating(uri)) {if (LOCAL_LOGV) {Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");}try {PDU_CACHE_INSTANCE.wait();} catch (InterruptedException e) {Log.e(TAG, "persist1: ", e);}}}PDU_CACHE_INSTANCE.purge(uri);PduHeaders header = pdu.getPduHeaders();PduBody body = null;ContentValues values = new ContentValues();Set<Entry<Integer, String>> set;set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();for (Entry<Integer, String> e : set) {int field = e.getKey();EncodedStringValue encodedString = header.getEncodedStringValue(field);if (encodedString != null) {String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);values.put(e.getValue(), toIsoString(encodedString.getTextString()));values.put(charsetColumn, encodedString.getCharacterSet());}}set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();for (Entry<Integer, String> e : set){byte[] text = header.getTextString(e.getKey());if (text != null) {values.put(e.getValue(), toIsoString(text));}}set = OCTET_COLUMN_NAME_MAP.entrySet();for (Entry<Integer, String> e : set){int b = header.getOctet(e.getKey());if (b != 0) {values.put(e.getValue(), b);}}set = LONG_COLUMN_NAME_MAP.entrySet();for (Entry<Integer, String> e : set){long l = header.getLongInteger(e.getKey());if (l != -1L) {values.put(e.getValue(), l);}}HashMap<Integer, EncodedStringValue[]> addressMap =new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);// Save address information.for (int addrType : ADDRESS_FIELDS) {EncodedStringValue[] array = null;if (addrType == PduHeaders.FROM) {EncodedStringValue v = header.getEncodedStringValue(addrType);if (v != null) {array = new EncodedStringValue[1];array[0] = v;}} else {array = header.getEncodedStringValues(addrType);}addressMap.put(addrType, array);}HashSet<String> recipients = new HashSet<String>();int msgType = pdu.getMessageType();if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)|| (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)|| (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {switch (msgType) {case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:loadRecipients(PduHeaders.FROM, recipients, addressMap, false);if (groupMmsEnabled) {loadRecipients(PduHeaders.TO, recipients, addressMap, true);loadRecipients(PduHeaders.CC, recipients, addressMap, true);}break;case PduHeaders.MESSAGE_TYPE_SEND_REQ:loadRecipients(PduHeaders.TO, recipients, addressMap, false);break;}long threadId = 0;if (createThreadId && !recipients.isEmpty()) {threadId = Threads.getOrCreateThreadId(mContext, recipients);}values.put(Mms.THREAD_ID, threadId);}long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.boolean textOnly = true;int messageSize = 0;if (pdu instanceof MultimediaMessagePdu) {body = ((MultimediaMessagePdu) pdu).getBody();// Start saving parts if necessary.if (body != null) {int partsNum = body.getPartsNum();if (partsNum > 2) {textOnly = false;}for (int i = 0; i < partsNum; i++) {PduPart part = body.getPart(i);messageSize += part.getDataLength();persistPart(part, dummyId, preOpenedFiles);String contentType = getPartContentType(part);if (contentType != null && !ContentType.APP_SMIL.equals(contentType)&& !ContentType.TEXT_PLAIN.equals(contentType)) {textOnly = false;}}}}values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {values.put(Mms.MESSAGE_SIZE, messageSize);}Uri res = null;if (existingUri) {res = uri;SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);} else {//Uri对应如果是Pdu表就是插入pdu如果是part表就更新part表res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); //解析Byte数字后进行插入到PDU表或part表if (res == null) {throw new MmsException("persist() failed: return null.");}msgId = ContentUris.parseId(res);}values = new ContentValues(1);values.put(Part.MSG_ID, msgId);SqliteWrapper.update(mContext, mContentResolver,Uri.parse("content://mms/" + dummyId + "/part"),values, null, null);if (!existingUri) {res = Uri.parse(uri + "/" + msgId);}// Save address information.for (int addrType : ADDRESS_FIELDS) {EncodedStringValue[] array = addressMap.get(addrType);if (array != null) {persistAddress(msgId, addrType, array);}}return res;}
mmssms.db中PDU表经过 persister.persist方法PDU是插入了数据的
在这一步进行只是告诉APP有一条彩信等待这去下载
下载步骤是开启TransactionService.java
PushReceiver.java
// Start service to finish the notification transaction.Intent svc = new Intent(mContext, TransactionService.class);svc.putExtra(TransactionBundle.URI, uri.toString());svc.putExtra(TransactionBundle.TRANSACTION_TYPE,Transaction.NOTIFICATION_TRANSACTION);svc.putExtra(Mms.SUBSCRIPTION_ID, subId);mContext.startService(svc);
TransactionService.java
这里主要看handler
@Overridepublic void handleMessage(Message msg) {LogTag.debugD("Handling incoming message: " + msg + " = " + decodeMessage(msg));Transaction transaction = null;switch (msg.what) {case EVENT_NEW_INTENT:onNewIntent((Intent)msg.obj, msg.arg1);break;case EVENT_QUIT:getLooper().quit();return;case EVENT_TRANSACTION_REQUEST: //通知彩信接收int serviceId = msg.arg1;try {TransactionBundle args = (TransactionBundle) msg.obj;TransactionSettings transactionSettings;LogTag.debugD("EVENT_TRANSACTION_REQUEST MmscUrl=" +args.getMmscUrl() + " proxy port: " + args.getProxyAddress());// Set the connection settings for this transaction.// If these have not been set in args, load the default settings.String mmsc = args.getMmscUrl();if (mmsc != null) {transactionSettings = new TransactionSettings(mmsc, args.getProxyAddress(), args.getProxyPort());} else {transactionSettings = new TransactionSettings(TransactionService.this, null,args.getSubId());}int transactionType = args.getTransactionType();// Create appropriate transactionswitch (transactionType) {case Transaction.NOTIFICATION_TRANSACTION:String uri = args.getUri();//用户通知if (uri!=null){long threadId = MessagingNotification.getThreadId(getApplicationContext(), Uri.parse(uri));MessagingNotification.blockingUpdateNewMessageIndicator(getApplicationContext(),threadId,false);MessagingNotification.updateDownloadFailedNotification(getApplicationContext());MessageUtils.updateThreadAttachTypeByThreadId(getApplicationContext(), threadId);new AsyncHandler(getContentResolver(),threadId).startQuery(0,null,mAllCanonical,mProjection,"_id="+threadId,null,null);if (!SharePreferencesUtils.getBoolean(getApplicationContext(),SMART_CAT_AGREEMENT)) {SharePreferencesUtils.putBoolean(getApplicationContext(),SMART_CAT_AGREEMENT,true);}}if (uri != null) {//开始解压消息 自动解压彩信transaction = new NotificationTransaction(TransactionService.this, serviceId,transactionSettings, uri);} else {byte[] pushData = args.getPushData();PduParser parser = new PduParser(pushData,PduParserUtil.shouldParseContentDisposition());GenericPdu ind = parser.parse();int type = PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;if ((ind != null) && (ind.getMessageType() == type)) {transaction = new NotificationTransaction(TransactionService.this, serviceId,transactionSettings, (NotificationInd) ind);} else {Log.e(TAG, "Invalid PUSH data.");transaction = null;return;}}break;case Transaction.RETRIEVE_TRANSACTION://手动下载短信transaction = new RetrieveTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;case Transaction.SEND_TRANSACTION:transaction = new SendTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;case Transaction.READREC_TRANSACTION:transaction = new ReadRecTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;default:Log.w(TAG, "Invalid transaction type: " + serviceId);transaction = null;return;}//copy the subId from TransactionBundle to transaction obj.transaction.setSubId(args.getSubId());//启动线程,进行下载,或发送操作if (!processTransaction(transaction)) {transaction = null;return;}LogTag.debugD("Started processing of incoming message: " + msg);} catch (Exception ex) {Log.w(TAG, "Exception occurred while handling message: " + msg, ex);if (transaction != null) {try {transaction.detach(TransactionService.this);if (mProcessing.contains(transaction)) {synchronized (mProcessing) {mProcessing.remove(transaction);}}} catch (Throwable t) {Log.e(TAG, "Unexpected Throwable.", t);} finally {// Set transaction to null to allow stopping the// transaction service.transaction = null;}}} finally {if (transaction == null) {LogTag.debugD("Transaction was null. Stopping self: " + serviceId);endMmsConnectivity();stopSelfIfIdle(serviceId);}}return;case EVENT_HANDLE_NEXT_PENDING_TRANSACTION:processPendingTransaction(transaction, (TransactionSettings) msg.obj);return;case EVENT_MMS_CONNECTIVITY_TIMEOUT:removeMessages(EVENT_MMS_CONNECTIVITY_TIMEOUT);mMmsConnecvivityRetryCount++;if (mMmsConnecvivityRetryCount > MMS_CONNECTIVITY_RETRY_TIMES) {Log.d(TAG, "MMS_CONNECTIVITY_TIMEOUT");mMmsConnecvivityRetryCount = 0;return;}if (!mPending.isEmpty()) {try {beginMmsConnectivity(SubscriptionManager.getDefaultDataSubscriptionId());} catch (IOException e) {Log.w(TAG, "Attempt to use of MMS connectivity failed");return;}}return;case EVENT_MMS_PDP_ACTIVATION_TIMEOUT:onPDPTimeout((int)msg.obj);return;default:return;}}
看这个方法
processTransaction(transaction)
private boolean processTransaction(Transaction transaction) throws IOException {// Check if transaction already processingsynchronized (mProcessing) {for (Transaction t : mPending) {if (t.isEquivalent(transaction)) {LogTag.debugD("Transaction already pending: " +transaction.getServiceId());return true;}}for (Transaction t : mProcessing) {if (t.isEquivalent(transaction)) {LogTag.debugD("Duplicated transaction: " + transaction.getServiceId());return true;}}int subId = transaction.getSubId();int phoneId = SubscriptionManagerWrapper.getPhoneId(subId);boolean isRequestNetworkQueued = true;LogTag.debugD("processTransaction :subId="+subId+ "; phoneId = " + phoneId+ "; mPhoneCount = "+ mPhoneCount);for (int id =0; id < mPhoneCount; id++) {if ((id != phoneId) && (mMmsNetworkRequest[id] != null)) {isRequestNetworkQueued = false;break;}}LogTag.debugD("processTransaction :isRequestNetworkQueued="+isRequestNetworkQueued);if ((mProcessing.size() > 0) || !isRequestNetworkQueued) {LogTag.debugD("Adding transaction to 'mPending' list: " + transaction);mPending.add(transaction);return true;} else {beginMmsConnectivity(subId);if (!mIsAvailable[phoneId]) {mPending.add(transaction);LogTag.debugD("processTransaction: connResult=APN_REQUEST_STARTED, " +"defer transaction pending MMS connectivity");if (transaction instanceof SendTransaction) {LogTag.debugD("remove cache while deferring");MmsApp.getApplication().getPduLoaderManager().removePdu(((SendTransaction) transaction).mSendReqURI);}return true;}LogTag.debugD("Adding transaction to 'mProcessing' list: " + transaction);mProcessing.add(transaction);}}LogTag.debugD("processTransaction: starting transaction " + transaction);transaction.attach(TransactionService.this);transaction.process();return true;}}
在最后启动了 transaction.process();
开启了RetrieveTransaction.java
RetrieveTransaction是一个线程类
package com.android.mms.transaction;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.provider.Telephony.Mms;
import android.provider.Telephony.Mms.Inbox;
import android.text.TextUtils;
import android.util.Log;import com.android.mms.LogTag;
import com.android.mms.MmsConfig;
import com.android.mms.R;
import com.android.mms.ui.MessageUtils;
import com.android.mms.ui.MessagingPreferenceActivity;
import com.android.mms.util.DownloadManager;
import com.android.mms.util.Recycler;
import com.android.mms.widget.MmsWidgetProvider;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.AcknowledgeInd;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduComposer;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduParser;
import com.google.android.mms.pdu.PduPersister;
import com.google.android.mms.pdu.RetrieveConf;import java.io.IOException;public class RetrieveTransaction extends Transaction implements Runnable {private static final String TAG = LogTag.TAG;private static final boolean DEBUG = false;private static final boolean LOCAL_LOGV = false;private final Uri mUri;private final String mContentLocation;private boolean mLocked;private boolean isCancelMyself;static final String[] PROJECTION = new String[] {Mms.CONTENT_LOCATION,Mms.LOCKED};// The indexes of the columns which must be consistent with above PROJECTION.static final int COLUMN_CONTENT_LOCATION = 0;static final int COLUMN_LOCKED = 1;public RetrieveTransaction(Context context, int serviceId,TransactionSettings connectionSettings, String uri)throws MmsException {super(context, serviceId, connectionSettings);if (uri.startsWith("content://")) {mUri = Uri.parse(uri); // The Uri of the M-Notification.indmId = mContentLocation = getContentLocation(context, mUri);if (LOCAL_LOGV) {Log.v(TAG, "X-Mms-Content-Location: " + mContentLocation);}} else {throw new IllegalArgumentException("Initializing from X-Mms-Content-Location is abandoned!");}// Attach the transaction to the instance of RetryScheduler.attach(RetryScheduler.getInstance(context));}private String getContentLocation(Context context, Uri uri)throws MmsException {Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),uri, PROJECTION, null, null, null);mLocked = false;if (cursor != null) {try {if ((cursor.getCount() == 1) && cursor.moveToFirst()) {// Get the locked flag from the M-Notification.ind so it can be transferred// to the real message after the download.mLocked = cursor.getInt(COLUMN_LOCKED) == 1;return cursor.getString(COLUMN_CONTENT_LOCATION);}} finally {cursor.close();}}throw new MmsException("Cannot get X-Mms-Content-Location from: " + uri);}/** (non-Javadoc)* @see com.android.mms.transaction.Transaction#process()*/@Overridepublic void process() {new Thread(this, "RetrieveTransaction").start();}public void run() {try {DownloadManager downloadManager = DownloadManager.getInstance();//Obtain Message Size from M-Notification.ind for original MMSint msgSize = downloadManager.getMessageSize(mUri);// Change the downloading state of the M-Notification.ind.downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING);if (isCancelMyself) {DownloadManager.getInstance().markState(mUri,DownloadManager.STATE_TRANSIENT_FAILURE);return;}// Send GET request to MMSC and retrieve the response data. 下载彩信byte[] resp= getPdu(mContentLocation,mUri);if (isCancelMyself) {DownloadManager.getInstance().markState(mUri,DownloadManager.STATE_TRANSIENT_FAILURE);return;}// Parse M-Retrieve.conf 从网上获取到的byte数字 给PduParser进行解析RetrieveConf retrieveConf = (RetrieveConf) new PduParser(resp,PduParserUtil.shouldParseContentDisposition()).parse();if (null == retrieveConf) {throw new MmsException("Invalid M-Retrieve.conf PDU.");}Uri msgUri = null;if (isDuplicateMessage(mContext, retrieveConf, mContentLocation)) {// Mark this transaction as failed to prevent duplicate// notification to user.if (Log.isLoggable(LogTag.TRANSACTION, Log.DEBUG)) {Log.v(TAG, "RetrieveTransaction DuplicateMessage !!");}mTransactionState.setState(TransactionState.FAILED);mTransactionState.setContentUri(mUri);} else {// Store M-Retrieve.conf into Inbox//将数据给PduPersister进行解析 插入到Part表中 在retrieveConf中的byte数组包括全部东西,有彩信,文本。。。PduPersister persister = PduPersister.getPduPersister(mContext);msgUri = persister.persist(retrieveConf, Inbox.CONTENT_URI, true,MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);// Use local time instead of PDU timeContentValues values = new ContentValues(3);values.put(Mms.DATE, System.currentTimeMillis() / 1000L);Cursor c = mContext.getContentResolver().query(mUri,null, null, null, null);if (c != null) {try {if (c.moveToFirst()) {int subId = c.getInt(c.getColumnIndex(Mms.SUBSCRIPTION_ID));Log.d(TAG, "RetrieveTransaction: subId value is " + subId);values.put(Mms.SUBSCRIPTION_ID, subId);}} finally {c.close();}}// Update Message Size for Original MMS.values.put(Mms.MESSAGE_SIZE, msgSize);SqliteWrapper.update(mContext, mContext.getContentResolver(),msgUri, values, null, null);// The M-Retrieve.conf has been successfully downloaded.mTransactionState.setState(TransactionState.SUCCESS);mTransactionState.setContentUri(msgUri);// Remember the location the message was downloaded from.// Since it's not critical, it won't fail the transaction.// Copy over the locked flag from the M-Notification.ind in case// the user locked the message before activating the download.updateContentLocation(mContext, msgUri, mContentLocation, mLocked);}// Delete the corresponding M-Notification.ind.SqliteWrapper.delete(mContext, mContext.getContentResolver(),mUri, null, null);if (msgUri != null) {// Have to delete messages over limit *after* the delete above. Otherwise,// it would be counted as part of the total.Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, msgUri);MmsWidgetProvider.notifyDatasetChanged(mContext);}// Send ACK to the Proxy-Relay to indicate we have fetched the// MM successfully.// Don't mark the transaction as failed if we failed to send it.sendAcknowledgeInd(retrieveConf);} catch (Throwable t) {Log.e(TAG, Log.getStackTraceString(t));} finally {if (mTransactionState.getState() != TransactionState.SUCCESS) {if (isCancelMyself) {mTransactionState.setState(TransactionState.CANCELED);} else {mTransactionState.setState(TransactionState.FAILED);}mTransactionState.setContentUri(mUri);Log.e(TAG, "Retrieval failed.");}notifyObservers();}}private static boolean isDuplicateMessage(Context context, RetrieveConf rc, String location) {byte[] rawMessageId = rc.getMessageId();if (rawMessageId != null) {String messageId = new String(rawMessageId);String selection = "(" + Mms.MESSAGE_ID + " = ? AND "+ Mms.CONTENT_LOCATION + " = ? AND "+ Mms.MESSAGE_TYPE + " = ?)";String[] selectionArgs = new String[] { messageId, location,String.valueOf(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) };Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),Mms.CONTENT_URI, new String[] { Mms._ID, Mms.SUBJECT, Mms.SUBJECT_CHARSET },selection, selectionArgs, null);if (cursor != null) {try {if (cursor.getCount() > 0) {// A message with identical message ID and type found.// Do some additional checks to be sure it's a duplicate.return isDuplicateMessageExtra(cursor, rc);}} finally {cursor.close();}}}return false;}private static boolean isDuplicateMessageExtra(Cursor cursor, RetrieveConf rc) {// Compare message subjects, taking encoding into accountEncodedStringValue encodedSubjectReceived = null;EncodedStringValue encodedSubjectStored = null;String subjectReceived = null;String subjectStored = null;String subject = null;encodedSubjectReceived = rc.getSubject();if (encodedSubjectReceived != null) {subjectReceived = encodedSubjectReceived.getString();}for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {int subjectIdx = cursor.getColumnIndex(Mms.SUBJECT);int charsetIdx = cursor.getColumnIndex(Mms.SUBJECT_CHARSET);subject = cursor.getString(subjectIdx);int charset = cursor.getInt(charsetIdx);if (subject != null) {encodedSubjectStored = new EncodedStringValue(charset, PduPersister.getBytes(subject));}if (encodedSubjectStored == null && encodedSubjectReceived == null) {// Both encoded subjects are null - return truereturn true;} else if (encodedSubjectStored != null && encodedSubjectReceived != null) {subjectStored = encodedSubjectStored.getString();if (!TextUtils.isEmpty(subjectStored) && !TextUtils.isEmpty(subjectReceived)) {// Both decoded subjects are non-empty - compare themreturn subjectStored.equals(subjectReceived);} else if (TextUtils.isEmpty(subjectStored) && TextUtils.isEmpty(subjectReceived)) {// Both decoded subjects are "" - return truereturn true;}}}return false;}private void sendAcknowledgeInd(RetrieveConf rc) throws MmsException, IOException {// Send M-Acknowledge.ind to MMSC if required.// If the Transaction-ID isn't set in the M-Retrieve.conf, it means// the MMS proxy-relay doesn't require an ACK.byte[] tranId = rc.getTransactionId();if (tranId != null) {// Create M-Acknowledge.indAcknowledgeInd acknowledgeInd = new AcknowledgeInd(PduHeaders.CURRENT_MMS_VERSION, tranId);// insert the 'from' address per specString lineNumber = MessageUtils.getLocalNumber();acknowledgeInd.setFrom(new EncodedStringValue(lineNumber));if (false){// Pack M-Acknowledge.ind and send itif(MmsConfig.getNotifyWapMMSC()) {sendPdu(new PduComposer(mContext, acknowledgeInd).make(), mContentLocation);} else {sendPdu(new PduComposer(mContext, acknowledgeInd).make());}}}}private static void updateContentLocation(Context context, Uri uri,String contentLocation,boolean locked) {ContentValues values = new ContentValues(2);values.put(Mms.CONTENT_LOCATION, contentLocation);values.put(Mms.LOCKED, locked); // preserve the state of the M-Notification.ind lock.SqliteWrapper.update(context, context.getContentResolver(),uri, values, null, null);}@Overridepublic void abort() {Log.d(TAG, "markFailed = " + this);mTransactionState.setState(TransactionState.FAILED);mTransactionState.setContentUri(mUri);mFailReason = FAIL_REASON_CAN_NOT_SETUP_DATA_CALL;notifyObservers();}@Overridepublic int getType() {return RETRIEVE_TRANSACTION;}@Overridepublic void cancelTransaction(Uri uri) {if (mUri.equals(uri)) {isCancelMyself = true;}}
}
通过Http进行下载获取到Byte数字
// Send GET request to MMSC and retrieve the response data. 下载彩信
byte[] resp= getPdu(mContentLocation,mUri);
getPdu 里面的操作是网络操作,主要是到MMSC服务器中下载数据下载后返回的是一个Byte数组
// Parse M-Retrieve.conf 从网上获取到的byte数字 给PduParser进行解析
RetrieveConf retrieveConf = (RetrieveConf) new PduParser(resp,PduParserUtil.shouldParseContentDisposition()).parse();
// Store M-Retrieve.conf into Inbox//将数据给PduPersister进行解析 插入到Part表中 在retrieveConf中的byte数组包括全部东西,有彩信,文本。。。
PduPersister persister = PduPersister.getPduPersister(mContext);
msgUri = persister.persist(retrieveConf, Inbox.CONTENT_URI, true,MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);
这一步part表就有数据了彩信下载成功了