本文共 43636 字,大约阅读时间需要 145 分钟。
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,
PushReceiver.java 这个是一个广播
@Override public 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
收到彩信时 IntentIntent { 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{ 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(); } @Override protected Void doInBackground(Intent... intents) { Intent intent = intents[0]; // Get raw PDU push-data from the message and parse it byte[] 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 base Log.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; } @Override protected 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, HashMappreOpenedFiles) 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 > set; set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet(); for (Entry 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 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 e : set){ int b = header.getOctet(e.getKey()); if (b != 0) { values.put(e.getValue(), b); } } set = LONG_COLUMN_NAME_MAP.entrySet(); for (Entry e : set){ long l = header.getLongInteger(e.getKey()); if (l != -1L) { values.put(e.getValue(), l); } } HashMap addressMap = new HashMap (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 recipients = new HashSet (); 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
@Override public 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 transaction switch (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 processing synchronized (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.ind mId = 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() */ @Override public void process() { new Thread(this, "RetrieveTransaction").start(); } public void run() { try { DownloadManager downloadManager = DownloadManager.getInstance(); //Obtain Message Size from M-Notification.ind for original MMS int 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 time ContentValues 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 account EncodedStringValue 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 true return true; } else if (encodedSubjectStored != null && encodedSubjectReceived != null) { subjectStored = encodedSubjectStored.getString(); if (!TextUtils.isEmpty(subjectStored) && !TextUtils.isEmpty(subjectReceived)) { // Both decoded subjects are non-empty - compare them return subjectStored.equals(subjectReceived); } else if (TextUtils.isEmpty(subjectStored) && TextUtils.isEmpty(subjectReceived)) { // Both decoded subjects are "" - return true return 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.ind AcknowledgeInd acknowledgeInd = new AcknowledgeInd( PduHeaders.CURRENT_MMS_VERSION, tranId); // insert the 'from' address per spec String lineNumber = MessageUtils.getLocalNumber(); acknowledgeInd.setFrom(new EncodedStringValue(lineNumber)); if (false){ // Pack M-Acknowledge.ind and send it if(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); } @Override public void abort() { Log.d(TAG, "markFailed = " + this); mTransactionState.setState(TransactionState.FAILED); mTransactionState.setContentUri(mUri); mFailReason = FAIL_REASON_CAN_NOT_SETUP_DATA_CALL; notifyObservers(); } @Override public int getType() { return RETRIEVE_TRANSACTION; } @Override public 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表就有数据了彩信下载成功了
转载地址:https://liwangjiang.blog.csdn.net/article/details/93047580 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!