android 彩信接收到附件的下载原理分析

http://blog.sina.com.cn/s/blog_5da93c8f0100ytil.html

转: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)进行分发处理。

android <wbr>彩信接收到附件的下载原理分析

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

photo

   
    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)){
               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;

android <wbr>彩信接收到附件的下载原理分析
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处理其余待发送的彩信和发送下载完成的广播。

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

photo

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

photo

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值