1. 程式人生 > >高通android 7.0彩信傳送流程

高通android 7.0彩信傳送流程

ComposeMessageActivity.java
sendMessage WorkingMessage
send private void prepareForSave(boolean notify) {         // Make sure our working set of recipients is resolved         // to first-class Contact objects before we save.         syncWorkingRecipients();         if (hasMmsContentToSave()) {             ensureSlideshow();             syncTextToSlideshow();         }     } 因為private SlideshowModel mSlideshow;這個是跟資料庫中part表資料對應的,所以現在要傳送彩信,如果有內容,則要將其都新增到mSlideshow中。 if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
注意,例如你現在添加了一張圖片,則這個時候其不會馬上存入資料庫中,但是會存入到mSlideshow中,當簡訊傳送或者儲存為草稿的時候才寫入到資料庫中的。 final PduPersister persister = PduPersister.getPduPersister(mActivity); 彩信是依靠PduPersister來將彩信資料寫入到資料庫的。 final SlideshowModel slideshow = mSlideshow;
final CharSequence subject = mSubject; final boolean textOnly = mAttachmentType == TEXT; new Thread(new Runnable() {     @Override     public void run() {                     final SendReq sendReq = makeSendReq(conv, subject);                     // Make sure the text in slide 0 is no longer holding onto a reference to                     // the text in the message text box.                     slideshow.prepareForSend();                     sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly);                     updateSendStats(conv);                 } }, "WorkingMessage.send MMS").start();同樣開啟一個子執行緒來進行傳送彩信 private static SendReq makeSendReq(Conversation conv, CharSequence subject) {         String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);         SendReq req = new SendReq();         EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);         if (encodedNumbers != null) {             req.setTo(encodedNumbers);//將收件人寫入         }         if (!TextUtils.isEmpty(subject)) {             req.setSubject(new EncodedStringValue(subject.toString()));//將主題寫入         }         req.setDate(System.currentTimeMillis() / 1000L);//將當前時間寫入         return req; }
void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister,             SlideshowModel slideshow, SendReq sendReq, boolean textOnly) DraftCache.getInstance().setSavingDraft(true);設定儲存草稿,好像是進行互斥,所以彩信是先要儲存草稿,然後才進行傳送
mStatusListener.onPreMessageSent();//彩信傳送前更新簡訊UI
if (newMessage) {//此時為true                 // Write something in the database so the new message will appear as sending                 ContentValues values = new ContentValues();                 values.put(Mms.MESSAGE_BOX, Mms.MESSAGE_BOX_OUTBOX);//此時彩信型別為發件箱                 values.put(Mms.THREAD_ID, threadId);                 values.put(Mms.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);//簡訊型別為請求傳送                 if (textOnly) {                     values.put(Mms.TEXT_ONLY, 1);                 }                 if ((TelephonyManager.getDefault().getPhoneCount()) > 1) {                     values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId);                 } else {                     values.put(Mms.SUBSCRIPTION_ID,                             SubscriptionManager.getDefaultDataSubscriptionId());                 }                 mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI,                         values);//插入到發件箱中  }
同樣,彩信,pdu表中也有一個欄位是表示當前彩信的各種狀態的,就是msg_box欄位,這裡是發件箱,其型別type是PduHeaders.MESSAGE_TYPE_SEND_REQ,往資料庫pdu的發件箱寫入資料 mStatusListener.onMessageSent();//簡訊傳送後更新UI,此時顯示正在傳送中... 然後查詢所有處於發件箱的彩信,計算他們的附件總大小,看是否超出了限制(為什麼這裡有這個限制?) if (newMessage) {                 // Create a new MMS message if one hasn't been made yet.                 mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri,                         mActivity, null);//將mSlideshow的資料寫入到part表中             } else {                 // Otherwise, sync the MMS message in progress to disk.                 updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);//更新草稿             } ContentValues values = new ContentValues(1);         if ((TelephonyManager.getDefault().getPhoneCount()) > 1) {             values.put(Mms.SUBSCRIPTION_ID, mCurrentConvSubId);         } else {             values.put(Mms.SUBSCRIPTION_ID, SubscriptionManager.getDefaultDataSubscriptionId());         }         SqliteWrapper.update(mActivity, mContentResolver, mmsUri, values, null, null); 更新subId MessageSender sender = new MmsMessageSender(mActivity, mmsUri,                 slideshow.getCurrentMessageSize(), mCurrentConvSubId); 呼叫MmsMessageSender的sendMessage來發送彩信
MmsMessageSender.java
sendMessage
首先是繼續更新資料庫的一些資料 PduPersister p = PduPersister.getPduPersister(mContext);         GenericPdu pdu = p.load(mMessageUri);//從資料庫中載入URI為mMessageUri到記憶體中,所以對於彩信,與資料庫互動我們是直接用google提供的pdu,非常簡單,這個另外一個專題講。 SendReq sendReq = (SendReq) pdu; 傳送請求的資料型別
if (!mMessageUri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) {             // If the message is already in the outbox (most likely because we created a "primed"             // message in the outbox when the user hit send), then we have to manually put an             // entry in the pending_msgs table which is where TransacationService looks for             // messages to send. Normally, the entry in pending_msgs is created by the trigger:             // insert_mms_pending_on_update, when a message is moved from drafts to the outbox.             ContentValues values = new ContentValues(7);             values.put(PendingMessages.PROTO_TYPE, MmsSms.MMS_PROTO);             values.put(PendingMessages.MSG_ID, messageId);             values.put(PendingMessages.MSG_TYPE, pdu.getMessageType());             values.put(PendingMessages.ERROR_TYPE, 0);             values.put(PendingMessages.ERROR_CODE, 0);             values.put(PendingMessages.RETRY_INDEX, 0);             values.put(PendingMessages.DUE_TIME, 0);             SqliteWrapper.insert(mContext, mContext.getContentResolver(),                     PendingMessages.CONTENT_URI, values);         } else {             p.move(mMessageUri, Mms.Outbox.CONTENT_URI);         } 我們已經是處於發件箱中,所以走第一個分支,即在表pending_msgs中插入一條資料,表示是待發送彩信 Intent intent = new Intent(mContext, TransactionService.class);         intent.putExtra(Mms.SUBSCRIPTION_ID, mSubId);         mContext.startService(intent); 啟動TransactionService服務來發送彩信。 TransactionService.java
onStartCommand
第一次是EVENT_NEW_INTENT訊息 private ServiceHandler mServiceHandler;
handleMessage
onNewIntent((Intent)msg.obj, msg.arg1);
mConnMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
傳送彩信需要先檢測ConnectivityManager服務,這個不是資料連線服務 我們假設傳送環境OK,可以傳送彩信。 Cursor cursor = PduPersister.getPduPersister(this).getPendingMessages(                     System.currentTimeMillis()); 載入所有的pending簡訊 int columnIndexOfRetryIndex = cursor.getColumnIndexOrThrow(                             PendingMessages.RETRY_INDEX); 這個是重試次數 transactionType是 Transaction.SEND_TRANSACTION;
TransactionBundle args = new TransactionBundle(transactionType,                                         uri.toString(), subId); 此時uri是pdu的uri launchTransaction(serviceId, args, false);
Message msg = mServiceHandler.obtainMessage(EVENT_TRANSACTION_REQUEST);
case Transaction.SEND_TRANSACTION:                                 transaction = new SendTransaction(                                         TransactionService.this, serviceId,                                         transactionSettings, args.getUri());                                 break; if (!processTransaction(transaction)) {                             transaction = null;                             return;                         } mPending和mProcessing此時是沒有的 beginMmsConnectivity(subId);開始mms的資料連線
if (!mIsAvailable[phoneId]) {                     mPending.add(transaction);                     LogTag.debugD("processTransaction: connResult=APN_REQUEST_STARTED, " +                             "defer transaction pending MMS connectivity");                     return true;                 }如果當前sim卡不可用,就會將這條傳送記錄新增到mPending中 if (mProcessing.size() > 0) {                     LogTag.debugD("Adding transaction to 'mPending' list: " + transaction);                     mPending.add(transaction);                     return true;                 } else {                     LogTag.debugD("Adding transaction to 'mProcessing' list: " + transaction);                     mProcessing.add(transaction);                 }我們現在是走else,所以直接處理。所以這兩個的關係是,mPending是將要處理,而mProcessing是正在處理的。 transaction.attach(TransactionService.this);             transaction.process(); process走的是SendTransaction的process public void process() {         mThread = new Thread(this, "SendTransaction");         mThread.start();     } 所以啟動子執行緒傳送彩信,然後返回true,我們繼續看主執行緒。哦,直接return。 SendTransaction
run RateController rateCtlr = RateController.getInstance();
PduPersister persister = PduPersister.getPduPersister(mContext);             SendReq sendReq = (SendReq) persister.load(mSendReqURI); 從資料庫載入要傳送的彩信 重新設定時間,這才是要傳送的時間,然後更新資料庫 byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey),                                       new PduComposer(mContext, sendReq).make()); Transaction.java
 /**      * A common method to send a PDU to MMSC.      *      * @param token The token to identify the sending progress.      * @param pdu A byte array which contains the data of the PDU.      * @return A byte array which contains the response data.      *         If an HTTP error code is returned, an IOException will be thrown.      * @throws IOException if any error occurred on network interface or      *         an HTTP error code(>=400) returned from the server.      * @throws MmsException if pdu is null.      */根據註釋,就是利用這個傳送彩信的,pdu是傳送的彩信內容,token是當前傳送的一個唯一標識,而返回值則是傳送結果 protected byte[] sendPdu(long token, byte[] pdu) throws IOException, MmsException {         return sendPdu(token, pdu, mTransactionSettings.getMmscUrl());     } protected byte[] sendPdu(long token, byte[] pdu,             String mmscUrl) throws IOException, MmsException {         if (pdu == null) {             throw new MmsException();         }         return HttpUtils.httpConnection(                 mContext, token,                 mmscUrl,                 pdu, HttpUtils.HTTP_POST_METHOD,                 mTransactionSettings.isProxySet(),                 mTransactionSettings.getProxyAddress(),                 mTransactionSettings.getProxyPort());     } AndroidHttpClient往下看是利用AndroidHttpClient來發送。這個我們後面再看。
String respStr = new String(response);
我這裡是傳送失敗,看起來是亂碼 SendConf conf = (SendConf) new PduParser(response,                    PduParserUtil.shouldParseContentDisposition()).parse(); int respStatus = conf.getResponseStatus();//傳送失敗這裡是130,public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;             values.put(Mms.RESPONSE_STATUS, respStatus); if (respStatus != PduHeaders.RESPONSE_STATUS_OK) {//不是傳送成功就更新狀態                 SqliteWrapper.update(mContext, mContext.getContentResolver(),                                      mSendReqURI, values, null, null);                 Log.e(TAG, "Server returned an error code: " + respStatus);                 return;             } if (mTransactionState.getState() != TransactionState.SUCCESS) {                 mTransactionState.setState(TransactionState.FAILED);                 mTransactionState.setContentUri(mSendReqURI);                 Log.e(TAG, "Delivery failed.");             } String messageId = PduPersister.toIsoString(conf.getMessageId());             values.put(Mms.MESSAGE_ID, messageId);             SqliteWrapper.update(mContext, mContext.getContentResolver(),                                  mSendReqURI, values, null, null);             // Move M-Send.req from Outbox into Sent.             Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI);//傳送成功應該會將pending裡面的條目刪除了,應該在資料庫中自動操作的。 傳送成功會將彩信狀態從待發送轉為已經發送 notifyObservers();//這裡會對監聽者進行回撥,TransactionService的update會被回撥。這裡應該是RetryScheduler的update被呼叫。都有。Transaction extends Observable提供了attach
mProcessing.remove(transaction);
else if (mProcessing.isEmpty()) {                     LogTag.debugD("update: endMmsConnectivity");                     endMmsConnectivity(transaction.getSubId());                 }關閉mms的資料連線 Intent intent = new Intent(TRANSACTION_COMPLETED_ACTION);             TransactionState state = transaction.getState();             int result = state.getState();             intent.putExtra(STATE, result); case TransactionState.FAILED:
boolean failSetupDataCall = Transaction.FAIL_REASON_CAN_NOT_SETUP_DATA_CALL                             == transaction.getFailReason();這個是?  isLastRetry scheme.getRetryLimit()這裡有5次
if (type == Transaction.SEND_TRANSACTION) {                                 if (failSetupDataCall) {                                     mToastHandler.sendEmptyMessage(                                             TOAST_SETUP_DATA_CALL_FAILED_FOR_SEND); <string name="no_network_send_failed_retry">"無網路,傳送失敗,稍後自動重新發送。"</string>                                 } else {                                     mToastHandler.sendEmptyMessage(TOAST_SEND_FAILED_RETRY);<string name="send_failed_retry">"傳送失敗,稍後自動重新發送。"</string>                                 }                             } sendBroadcast(intent);
finally {             transaction.detach(this);             stopSelfIfIdle(serviceId);         } TRANSACTION_COMPLETED_ACTION 沒有發現哪個廣播會接收。
所以到這裡結束了。UI會根據資料庫的改變而重新整理介面。 彩信的重發機制 跟DefaultRetryScheme這個有關 <integer-array name="retry_scheme">         <item>0</item>         <item>60000</item>         <item>300000</item>         <item>600000</item>         <item>1800000</item>     </integer-array> getWaitingInterval
RetryScheduler.java
如果想了解彩信傳送過程中與pdu的關係,可以看部落格 如果發現本部落格對您們有幫助,幫忙頂下。謝謝了!!