android彩信接收到附件的下载原理分析 手机无法接收彩信

转:http://xiaoyuang.com/print.php?id=12586

首先,了解下彩信收发的宏观步骤:

a、 终端A向彩信中心(MMSC)发送一条彩信,通过WAP网关POST到MMSC

b、 MMSC通过PushProxy网关,向SMSC(短信中心)发送PUSH消息,SMSC转发到终端B

c、 终端B通过WAP网关利用GET方法从MMSC获取一条彩信

d、 MMSC通过PushProxy网关和SNSC向终端A发送一条传送报告(delivery report)

从上面这个步骤可以看出,彩信的接收分两个步骤:

1、接收到短信。

2、分析短信然后通过http来获取彩信附件。

(续我的另一篇博客:http://www.cnblogs.com/not-code/archive/2011/11/27/2265287.html)

因此彩信第一步跟短信的接收流程一样,在RILReceiver接收到短信转到processUnsolicited进行处理。

GSM 方式(最近才知道短信的收发有两种,一种就是通过GSM,另一种是通过CDMA):

GSM其事件类型为RIL_UNSOL_RESPONSE_NEW_SMS。先调用responseString从Parcel中获取数据,再使用newFromCMT方法获取SmsMessage对象,最后调用mSMSRegistrant的notifyRegistrant方法设置消息类型(what属性为EVENT_NEW_SMS)并转到SMSDispatcher进行处理。这个时候就会调用子类(GsmSMSDispatcher)的dispatchMessage方法处理。


protected int dispatchMessage(SmsMessageBase smsb) {
// If sms isnull, means there was a parsing error.
if (smsb == null) {
return Intents.RESULT_SMS_GENERIC_ERROR;
}
SmsMessage sms = (SmsMessage) smsb;
boolean handled = false;

if (sms.isTypeZero()) {
// As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should notbe
// Displayed/Stored/Notified. They should only beacknowledged.
Log.d(TAG, "Received short message type 0, Dont display or storeit. Send Ack");
return Intents.RESULT_SMS_HANDLED;
}

// Special case the message waiting indicator messages
if (sms.isMWISetMessage()) {
mGsmPhone.updateMessageWaitingIndicator(true);
handled = sms.isMwiDontStore();
if (Config.LOGD) {
Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" +!handled);
}
} else if (sms.isMWIClearMessage()) {
mGsmPhone.updateMessageWaitingIndicator(false);
handled = sms.isMwiDontStore();
if (Config.LOGD) {
Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" +!handled);
}
}

if (handled) {
return Intents.RESULT_SMS_HANDLED;
}

if (!mStorageAvailable &&(sms.getMessageClass() != MessageClass.CLASS_0)) {
// Its a storable message and theres no storage available.Bail.
// (See TS 23.038 for a description of class 0 messages.)
return Intents.RESULT_SMS_OUT_OF_MEMORY;
}

SmsHeader smsHeader = sms.getUserDataHeader();
// See if message is partial or port addressed.
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
// Message is not partial (not part of concatenatedsequence).
byte[][] pdus = new byte[1][];
pdus[0] = sms.getPdu();

if (smsHeader != null &&smsHeader.portAddrs != null) {
if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH){
return mWapPush.dispatchWapPdu(sms.getUserData());
} else {
// The message was sent to a port, so concoct a URI for it.
dispatchPortAddressedPdus(pdus,smsHeader.portAddrs.destPort);
}
} else {
// Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
return Activity.RESULT_OK;
} else {
// Process the message part.
return processMessagePart(sms, smsHeader.concatRef,smsHeader.portAddrs);
}
}

在这个方法里,将会判断接收到的短信是否长短信、是否彩信、是否普通短信。

首先获取SmsHeader,如果SmsHeader或SmsHeader.concatRef均不为空,说明是长短信,则调用processMessagePart将短信分段存入raw表,待所有分段都收到后,将其组装。然后根据端口的不同,按照彩信通知(WapPushOverSms的dispatchWapPdu方法)、指定端口的彩信(dispatchPortAddressedPdus)、长短信(dispatchPdus)进行分发处理。

继续分析彩信,如果是彩信就调用WapPushOverSms类dispatchWapPdu的方法。(该类的位置)


public intdispatchWapPdu(byte[] pdu) {

if (Config.LOGD) Log.d(LOG_TAG, "Rx: " +IccUtils.bytesToHexString(pdu));

int index = 0;
int transactionId = pdu[index++] &0xFF;
int pduType = pdu[index++] & 0xFF;
int headerLength = 0;

if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH)&&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type =" + pduType);
return Intents.RESULT_SMS_HANDLED;
}

pduDecoder = new WspTypeDecoder(pdu);


if (pduDecoder.decodeUintvarInteger(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Lengtherror.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int)pduDecoder.getValue32();
index += pduDecoder.getDecodedDataLength();

int headerStartIndex = index;


if (pduDecoder.decodeContentType(index) == false) {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Typeerror.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
int binaryContentType;
String mimeType = pduDecoder.getValueString();
if (mimeType == null) {
binaryContentType = (int)pduDecoder.getValue32();
// TODO we should have more generic way to map binaryContentTypecode to mimeType.
switch (binaryContentType) {
case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML;
break;
case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML:
mimeType =WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML;
break;
case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI;
break;
case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL;
break;
case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO;
break;
case WspTypeDecoder.CONTENT_TYPE_B_MMS:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS;
break;
case WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF:
mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF;
break;
default:
if (Config.LOGD) {
Log.w(LOG_TAG,
"Received PDU. Unsupported Content-Type = " +binaryContentType);
}
return Intents.RESULT_SMS_HANDLED;
}
} else {
if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)){
binaryContentType =WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML;
} else if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)){
binaryContentType =WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML;
} else if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)){
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI;
} else if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)){
android彩信接收到附件的下载原理分析 手机无法接收彩信
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL;
} else if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)){
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO;
// 彩信类型 CONTENT_MIME_TYPE_B_MMS=“application/vnd.wap.mms-message”

} else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)){
binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
} else if(mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF)){
binaryContentType =WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF;
} else {
if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type= " + mimeType);
return Intents.RESULT_SMS_HANDLED;
}
}
index += pduDecoder.getDecodedDataLength();

boolean dispatchedByApplication = false;
switch (binaryContentType) {
case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
dispatchWapPdu_PushCO(pdu, transactionId, pduType,headerStartIndex, headerLength);
dispatchedByApplication = true;
break;

//处理短信方法
case WspTypeDecoder.CONTENT_TYPE_B_MMS:
dispatchWapPdu_MMS(pdu, transactionId, pduType, headerStartIndex,headerLength);
dispatchedByApplication = true;
break;
default:
break;
}
if (dispatchedByApplication == false) {
dispatchWapPdu_default(pdu,transactionId, pduType, mimeType,
headerStartIndex, headerLength);
}
return Activity.RESULT_OK;
}

private void dispatchWapPdu_MMS(byte[]pdu, int transactionId, int pduType,
int headerStartIndex, int headerLength) {
byte[] header = new byte[headerLength];
System.arraycopy(pdu, headerStartIndex, header, 0,header.length);
int dataIndex = headerStartIndex + headerLength;
byte[] data = new byte[pdu.length - dataIndex];
System.arraycopy(pdu, dataIndex, data, 0, data.length);

Intent intent = newIntent(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS);
intent.putExtra("transactionId", transactionId);
intent.putExtra("pduType", pduType);
intent.putExtra("header", header);
intent.putExtra("data", data);

mSmsDispatcher.dispatch(intent,"android.permission.RECEIVE_MMS");
}

该方法比较长,我们直接看到:调用父类的dispatch将信息广播出去。

应用层packagecom.android.gzs.mms.transaction.PushReceiver类的onReceive将被调用,让屏幕亮5秒,

//接收彩信事件
@Override
public voidonReceive(Context context, Intent intent) {
if(intent.getAction().equals(WAP_PUSH_RECEIVED_ACTION)
&&ContentType.MMS_MESSAGE.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.
//让屏幕亮5秒,然后创建一个ReceivePushTask并使用它的execute方法。
PowerManagerpm =(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);
}
}
然后创建一个ReceivePushTask并使用它的execute方法。ReceivePushTask是一个AsyncTask,实现了doInBackground方法。当传入intent后,会在doInBackground中将其中的数据转成GenericPdu,并根据其消息类型做出不同的操作。

private class ReceivePushTask extendsAsyncTask<Intent,Void,Void> {
private Context mContext;
public ReceivePushTask(Context context) {
mContext = context;
}

@Override
protected Void doInBackground(Intent... intents) {
Intent intent = intents[0];

// 获取push-data
//Get raw PDU push-data from the message and parse it
byte[] pushData =intent.getByteArrayExtra("data");
PduParser parser = new PduParser(pushData);
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:
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;
}

Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
// 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;
//是否将TransactionID添加到URl的后面,
//TransactionId交易标识,用以识别一对M-Notification.ind及后续的M-NotifyResp.ind消息
if (MmsConfig.getTransIdEnabled()) {
//下载URI地址Content-location-value
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)) {
Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
// 启动TransactionService服务,在onStartCommand中调用launchTransaction方法。
Intent svc = new Intent(mContext,TransactionService.class);
svc.putExtra(TransactionBundle.URI,uri.toString());
svc.putExtra(TransactionBundle.TRANSACTION_TYPE,
Transaction.NOTIFICATION_TRANSACTION);
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;
}
}
如果是发送报告或已读报告,将其存入数据库。
如果是彩信通知,若已存在,则不处理。否则将其存入数据库。启动TransactionService进行处理。

启动TransactionService服务,在onStartCommand中调用launchTransaction方法。

@Override
public intonStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
return Service.START_NOT_STICKY;
}
mConnMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
boolean noNetwork = !isNetworkAvailable();

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "onStart: #" + startId + ": " + intent.getExtras() + "intent=" + intent);
Log.v(TAG, "networkAvailable=" + !noNetwork);
}

if (ACTION_ONALARM.equals(intent.getAction()) ||(intent.getExtras() == null)) {
// Scan database to find all pending operations.
Cursor cursor =PduPersister.getPduPersister(this).getPendingMessages(
System.currentTimeMillis());
if (cursor != null) {
try {
int count = cursor.getCount();

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "onStart: cursor.count=" + count);
}

if (count == 0) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "onStart: no pending messages. Stoppingservice.");
}
RetryScheduler.setRetryAlarm(this);
stopSelfIfIdle(startId);
return Service.START_NOT_STICKY;
}

int columnIndexOfMsgId =cursor.getColumnIndexOrThrow(PendingMessages.MSG_ID);
int columnIndexOfMsgType = cursor.getColumnIndexOrThrow(
PendingMessages.MSG_TYPE);

if (noNetwork) {
// Make sure we register for connection state changes.
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "onStart: registerForConnectionStateChanges");
}
MmsSystemEventReceiver.registerForConnectionStateChanges(
getApplicationContext());
}

while (cursor.moveToNext()) {
int msgType = cursor.getInt(columnIndexOfMsgType);
int transactionType = getTransactionType(msgType);
if (noNetwork) {
onNetworkUnavailable(startId, transactionType);
return Service.START_NOT_STICKY;
}
switch (transactionType) {
case -1:
break;
case Transaction.RETRIEVE_TRANSACTION:
// If it's a transiently failed transaction,
// we should retry it in spite of current
// downloading mode.
int failureType = cursor.getInt(
cursor.getColumnIndexOrThrow(
PendingMessages.ERROR_TYPE));
if (!isTransientFailure(failureType)) {
break;
}
// fall-through
default:
Uri uri = ContentUris.withAppendedId(
Mms.CONTENT_URI,
cursor.getLong(columnIndexOfMsgId));
TransactionBundle args = new TransactionBundle(
transactionType, uri.toString());
// FIXME: We use the same startId for all MMs.
launchTransaction(startId, args, false);
break;
}
}
} finally {
cursor.close();
}
} else {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "onStart: no pending messages. Stoppingservice.");
}
RetryScheduler.setRetryAlarm(this);
stopSelfIfIdle(startId);
}
} else {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "onStart: launch transaction...");
}
// For launching NotificationTransaction and testpurpose.
TransactionBundle args = newTransactionBundle(intent.getExtras());
launchTransaction(startId, args, noNetwork);
}
return Service.START_NOT_STICKY;
}

private void launchTransaction(int serviceId, TransactionBundletxnBundle, boolean noNetwork) {
if (noNetwork) {
Log.w(TAG, "launchTransaction: no network error!");
onNetworkUnavailable(serviceId,txnBundle.getTransactionType());
return;
}
Messagemsg = mServiceHandler.obtainMessage(EVENT_TRANSACTION_REQUEST);
msg.arg1 = serviceId;
msg.obj = txnBundle;

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "launchTransaction: sending message " + msg);
}
mServiceHandler.sendMessage(msg);
}

接着以what=EVENT_TRANSACTION_REQUEST的参数运行mServiceHandler,在mServiceHandler的处理中将创建NotificationTransaction类的对象,经一系列的判断最后将调用processTransaction方法处理NotificationTransaction对象。

privateServiceHandler mServiceHandler;


handMessage()方法处理:

public void handleMessage(Message msg){
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "Handling incoming message: " + msg);
}

Transaction transaction = null;

switch (msg.what) {
case EVENT_QUIT:
getLooper().quit();
return;

case EVENT_CONTINUE_MMS_CONNECTIVITY:
synchronized (mProcessing) {
if (mProcessing.isEmpty()) {
return;
}
}

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "handle EVENT_CONTINUE_MMS_CONNECTIVITYevent...");
}

try {
int result = beginMmsConnectivity();
if (result != Phone.APN_ALREADY_ACTIVE) {
Log.v(TAG, "Extending MMS connectivity returned " + result+
" instead of APN_ALREADY_ACTIVE");
// Just wait for connectivity startup without
// any new request of APN switch.
return;
}
} catch (IOException e) {
Log.w(TAG, "Attempt to extend use of MMS connectivityfailed");
return;
}

// Restart timer
sendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),
APN_EXTENSION_WAIT);
return;

case EVENT_DATA_STATE_CHANGED:

if (mConnectivityListener == null) {
return;
}

NetworkInfo info =mConnectivityListener.getNetworkInfo();
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "Handle DATA_STATE_CHANGED event: " +info);
}

// Check availability of the mobile network.
if ((info == null) || (info.getType() !=
ConnectivityManager.TYPE_MOBILE_MMS)) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, " type is notTYPE_MOBILE_MMS, bail");
}
return;
}

if (!info.isConnected()) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, " TYPE_MOBILE_MMSnot connected, bail");
}
return;
}

TransactionSettings settings = newTransactionSettings(
TransactionService.this, info.getExtraInfo());

// If this APN doesn't have an MMSC, wait for one thatdoes.
if (TextUtils.isEmpty(settings.getMmscUrl())) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, " empty MMSC url,bail");
}
return;
}

// Set a timer to keep renewing our "lease" on the MMSconnection
sendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),
APN_EXTENSION_WAIT);
processPendingTransaction(transaction, settings);
return;

case EVENT_TRANSACTION_REQUEST:
int serviceId = msg.arg1;
try {
TransactionBundle args = (TransactionBundle)msg.obj;
TransactionSettings transactionSettings;

// Set the connection settings for thistransaction.
// If these have not been set in args, load the defaultsettings.
String mmsc = args.getMmscUrl();
if (mmsc != null) {
transactionSettings = new TransactionSettings(
mmsc, args.getProxyAddress(),args.getProxyPort());
} else {
transactionSettings = new TransactionSettings(
TransactionService.this, null);
}

int transactionType = args.getTransactionType();

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "handle EVENT_TRANSACTION_REQUEST: transactionType="+
transactionType);
}

// Create appropriate transaction
switch (transactionType) {
case Transaction.NOTIFICATION_TRANSACTION:
String uri = args.getUri();
if (uri != null) {
transaction = newNotificationTransaction(
TransactionService.this, serviceId,
transactionSettings, uri);
} else {
// Now it's only used for test purpose.
byte[] pushData = args.getPushData();
PduParser parser = new PduParser(pushData);
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;
}

if (!processTransaction(transaction)) {
transaction = null;
return;
}

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "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) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)){
Log.v(TAG, "Transaction was null. Stopping self: " +serviceId);
}
endMmsConnectivity();
stopSelf(serviceId);
}
}
return;
case EVENT_HANDLE_NEXT_PENDING_TRANSACTION:
processPendingTransaction(transaction, (TransactionSettings)msg.obj);
return;
default:
Log.w(TAG, "what=" + msg.what);
return;
}
}


接着以what=EVENT_TRANSACTION_REQUEST的参数运行mServiceHandler,在mServiceHandler的处理中将创建NotificationTransaction类的对象,经一系列的判断最后将调用processTransaction方法处理NotificationTransaction对象。

private boolean processTransaction(Transaction transaction) throwsIOException {
// Check if transaction already processing
synchronized (mProcessing) {
for (Transaction t : mPending) {
if (t.isEquivalent(transaction)) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "Transaction already pending: " +
transaction.getServiceId());
}
return true;
}
}
for (Transaction t : mProcessing) {
if (t.isEquivalent(transaction)) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "Duplicated transaction: " +transaction.getServiceId());
}
return true;
}
}


if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "processTransaction: callbeginMmsConnectivity...");
}
int connectivityResult = beginMmsConnectivity();
if (connectivityResult == Phone.APN_REQUEST_STARTED) {
mPending.add(transaction);
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "processTransaction: connResult=APN_REQUEST_STARTED, "+
"defer transaction pending MMS connectivity");
}
return true;
}

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "Adding transaction to 'mProcessing' list: " +transaction);
}
mProcessing.add(transaction);
}

// Set a timer to keep renewing our "lease" on the MMSconnection
sendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),
APN_EXTENSION_WAIT);

if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "processTransaction: starting transaction " +transaction);
}

// Attach to transaction and process it
transaction.attach(TransactionService.this);
transaction.process();
return true;
}
}

该方法首先会先判断该Transaction对象是否存在mPending或mProcessing队列中,如果没有则将对象加入到mProcessing中,并将TransactionService本身加入到NotificationTransaction对象的观察者列表(这样做的目的是为了后面下载完成后通知该服务TransactionService的mProcessing移除掉NotificationTransaction对象并发送完成下载的广播)。最后将调用NotificationTransaction的process方法。

@Override
public voidprocess() {
new Thread(this).start();
}

public voidrun() {
DownloadManager downloadManager =DownloadManager.getInstance();
//是否自动下载彩信
boolean autoDownload = downloadManager.isAuto();
//是否延迟接收
boolean dataSuspended =(MmsApp.getApplication().getTelephonyManager().getDataState()==
TelephonyManager.DATA_SUSPENDED);
try {
if (LOCAL_LOGV) {
Log.v(TAG, "Notification transaction launched: " + this);
}

// By default, we set status to STATUS_DEFERRED because we
// should response MMSC with STATUS_DEFERRED when we cannot
// download a MM immediately.
int status = STATUS_DEFERRED;//延缓状态
//只有彩信的设置是自动获取(“auto retrieve”)时,它才会去下载彩信,否则,它只处理彩信通知(NotificationIndication),而不去下载彩信。
// Don't try to download when data is suspended, as it will fail,so defer download
if (!autoDownload || dataSuspended) {
//标记短信状态【延迟下载】
downloadManager.markState(mUri,DownloadManager.STATE_UNSTARTED);
//发送回执信息
sendNotifyRespInd(status);
return;
}
//【已下载】
downloadManager.markState(mUri,DownloadManager.STATE_DOWNLOADING);

if (LOCAL_LOGV) {
Log.v(TAG, "Content-Location: " + mContentLocation);
}

byte[] retrieveConfData = null;
// We should catch exceptions here to response MMSC
// with STATUS_DEFERRED.
try {
// 接收PDU 数据retrieve a PDU from MMSC.
retrieveConfData = getPdu(mContentLocation);
} catch (IOException e) {
mTransactionState.setState(FAILED);
}

if (retrieveConfData != null) {
GenericPdu pdu = new PduParser(retrieveConfData).parse();
if ((pdu == null) || (pdu.getMessageType() !=MESSAGE_TYPE_RETRIEVE_CONF)) {
Log.e(TAG, "Invalid M-RETRIEVE.CONF PDU.");
mTransactionState.setState(FAILED);
status = STATUS_UNRECOGNIZED;
} else {
// Save the received PDU (must be a M-RETRIEVE.CONF).
PduPersister p = PduPersister.getPduPersister(mContext);
Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
// We have successfully downloaded the new MM. Delete the
// M-NotifyResp.ind from Inbox.
SqliteWrapper.delete(mContext, mContext.getContentResolver(),
mUri, null, null);
// Notify observers with newly received MM.
mUri = uri;
status = STATUS_RETRIEVED;
}
}

if (LOCAL_LOGV) {
Log.v(TAG, "status=0x" + Integer.toHexString(status));
}

// Check the status and update the result state of thisTransaction.
switch (status) {
case STATUS_RETRIEVED:
mTransactionState.setState(SUCCESS);
break;
case STATUS_DEFERRED:
// STATUS_DEFERRED, may be a failed immediate retrieval.
if (mTransactionState.getState() == INITIALIZED) {
mTransactionState.setState(SUCCESS);
}
break;
}

sendNotifyRespInd(status);

// Make sure this thread isn't over the limits in messagecount.
Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext,mUri);
} catch (Throwable t) {
Log.e(TAG, Log.getStackTraceString(t));
} finally {
mTransactionState.setContentUri(mUri);
if (!autoDownload || dataSuspended) {
// Always mark the transaction successful for deferred
// download since any error here doesn't make sense.
mTransactionState.setState(SUCCESS);
}
if (mTransactionState.getState() != SUCCESS) {
mTransactionState.setState(FAILED);
Log.e(TAG, "NotificationTransaction failed.");
}
notifyObservers();
}
}

NotificationTransaction的process方法将下载相应彩信,首先删除彩信通知,通知mmsc,删除超过容量限制的彩信,彩信附件的获取最终是通过getPdu(mContentLocation)来请求附件的流,返回byte[]类型。最后将notifyObservers()通知TransactionService处理其余待发送的彩信和发送下载完成的广播。

看图比较容易理解:(网上窃的一副妙图~~,不过里面最后那里好像有一个小错误,不过没关系~~)

(彩信接收主要大概涉及到的类的类图,这是原创滴,原创就是简陋)

RIL到SMSDispatch中间其实涉及很多步骤,这里就简明一点,抽丝剥茧,让大家对彩信接收涉及到的类的分布和以及它们的作用有个大概的蓝图。短信来后发现自己属于GsmSMSDispatch类,WapPushOverSms实例调用dispatchWapPdu发送广播,PushReceive接收到广播后启动TransactionService服务,TransactionService将自己attach到mObservers的观察列表中,然后调用NotificationTransaction对象的process方法,该方法将通过getPdu来获取附件内容,最后将调用notifyObservers通知所有添加到观察列表的对象调用update方法实现更新。

  

爱华网本文地址 » http://www.413yy.cn/a/25101012/133352.html

更多阅读

微信搜不到附近的人有哪些原因? 微信附近人搜不到

微信搜不到附近的人有哪些原因?——简介这种情况应该是大家经常会遇到的,那就是打开微信搜索附近的人,但是半天也没有搜索结果,综合起来,小编认为有以下几种可能:微信搜不到附近的人有哪些原因?——工具/原料智能手机微信APP微信搜不到附

windows-7纯净版镜像文件的下载 windows10纯净版镜像

windows-7纯净版镜像文件的下载——简介xp停止服务支持,升级到windows-7是不错的选择。但网上和电脑店里出售的windows-7光盘镜像都捆绑着许多根本用不上的软件,下载安装后不但占用硬盘空间,拖慢电脑运行速度,还可能隐藏着未知的木马病

ADT的下载和配置 adt配置sdk路径

ADT的下载和配置——简介ADT:Android Development Tools, Eclipse的Android 开发插件,把Eclipse和SDK联系起来。ADT的下载和配置——工具/原料SDK环境ADTADT的下载和配置——方法/步骤ADT的下载和配置 1、可在各大型软件网载ADT,在Ecli

QQ空间看不到好友的动态怎么办 微信看不到好友动态

QQ空间看不到好友的动态怎么办——简介最近朋友因为误操作,致QQ空间的好友动态无法显示,找到本人相助,可以肯定的是设置问题,所以本人稍微摸索一下,解决了这个问题,现将此方法分享出来。QQ空间看不到好友的动态怎么办——方法/步骤QQ空间

微信搜索不到附的人怎么办 微信附近人怎么解封

微信搜索不到附的人怎么办——简介怎么解决微信搜索不到附近联系人呢,首大家平时在用的时候是否也出现过类似的情况,解决的办法是首先打开你的手机检查网络是否正确或更改一下接入点名称、在修改一下昵称、再看看你手机是否已经关闭了

声明:《android彩信接收到附件的下载原理分析 手机无法接收彩信》为网友玻璃碴子分享!如侵犯到您的合法权益请联系我们删除