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   这个是一个广播

@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

收到彩信时 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();
}


@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,
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

@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表就有数据了彩信下载成功了