Android6.0的SMS(短信)源码分析--短信发送
1?????SMS發送流程
1.1???SmsManager
Android發送短信的接口可以認為是SmsManager,當然并不是所有的App都可以發送短信的,必須配置相關的權限。App中可以通過SmsManager.getDefault()得到SmsManager的單例。首先來SmsManager主要提供的接口有哪些。
| public static SmsManager | getDefault()獲取?SmsManager?的默認實例 |
| public void | sendTextMessage( ??????????? String destinationAddress, String scAddress, String text, ??????????? PendingIntent sentIntent, PendingIntent deliveryIntent) { ??????? sendTextMessageInternal(destinationAddress, scAddress, text, ??????????? sentIntent, deliveryIntent, true?/* persistMessageForCarrierApp*/); ??? }發送一個基于?SMS?的文本 |
| public void | sendDataMessage( ??????????? String destinationAddress, String scAddress, short destinationPort, ??????????? byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) 發送一個基于?SMS?的數據到指定的應用程序端口 |
| public ArrayList<String> | divideMessage(String text)?當短信超過?SMS?消息的最大長度時,將短信分割為幾塊。 |
| public void | sendMultipartTextMessage( ??????????? String destinationAddress, String scAddress, ArrayList<String> parts, ??????????? ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents)?發送一個基于SMS的多部分文本,調用者應用已經通過調用divideMessage?(String text)將消息分割成正確的大小 |
我們知道短信有長短信和普通短信的區別。長短信一般需要做分隔處理,而普通短信則不需要。這里重點研究的是普通短信。
普通短信的發送接口為SmsManager. sendTextMessage()。其函數原型和參數解釋如下:
| public void?sendTextMessage(String?destinationAddress,?String?scAddress,?String?text,PendingIntent?sentIntent,? PendingIntent?deliveryIntent) destinationAddress:?收件人地址? |
下面是SmsManager. sendTextMessage()的具體實現。
| ??? public void sendTextMessage( ??????????? String destinationAddress, String scAddress, String text, ??????????? PendingIntent sentIntent, PendingIntent deliveryIntent) { ????????sendTextMessageInternal(destinationAddress, scAddress, text, ????????????sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/); ??? } |
可以看到僅僅是轉調了內部方法sendTextMessageInternal()。
| ??? private void sendTextMessageInternal(String destinationAddress, String scAddress, ??????????? String text, PendingIntent sentIntent, PendingIntent deliveryIntent, ??????????? boolean persistMessageForCarrierApp) { ??????? if (TextUtils.isEmpty(destinationAddress)) {//對目的地址進行非空判斷 ??????????? throw new IllegalArgumentException("Invalid destinationAddress"); ??????? } ? ??????? if (TextUtils.isEmpty(text)) {//對發送的內容進行非空判斷 ??????????? throw new IllegalArgumentException("Invalid message body"); ??????? } ? ??????? try { ????????????ISms iccISms = getISmsServiceOrThrow();//獲取isim服務 ????????????iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), ????????????????????destinationAddress, ????????????????????scAddress, text, sentIntent, deliveryIntent, ????????????????????persistMessageForCarrierApp); ??????? } catch (RemoteException ex) { ????????????// ignore it ??????? } ??? } |
sendTextMessageInternal()首先是獲取了isms系統服務,然后調用了其sendTextForSubscriber()方法。這里可以看出Andorid的一貫風格:App總是將某個任務交給有能力完成該任務的服務去執行。而這里這個有能力完成短信發送任務的系統服務其實就是UiccSmsController(從其構造方法可以看出來它就是isim service),因此進入UiccSmsController.sendTextForSubscriber()。
| ??? public void sendTextForSubscriber(int subId, String callingPackage, String destAddr, ??????????? String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, ??????????? boolean persistMessageForNonDefaultSmsApp) { ????????IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId); ??????? if (iccSmsIntMgr != null) { ????????????iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent, ????????????????????deliveryIntent, persistMessageForNonDefaultSmsApp); ??????? } else { ??????????? Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" + ????????????????????????? " Subscription: " + subId); ??????????? sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE); ??????? } ??? } |
首先是通過subId獲得對應的IccSmsInterfaceManager,然后調用了其sendText方法。里的subId是調用者SmsManager以參數的形式傳進來的。通過getSubscriptionId()獲取,其主要的邏輯就是如果設置成了默認方式,那就返回默認卡的subId。如果設置成了默認方式但并沒有設置默認卡,則發intent提示用戶設置。如果沒有設置為默認方式,而是通過SmsManager的構造參數傳遞進來的,則直接返回這個參數的值就行了。還有一種可能就是非默認,且SmsManager又是通過getDegault的方式得到的,那這個subId就可能會根據時間變化了,并且可能返回負數。我們不管這里如何得到卡的subId,直接進入ICCSmsInterface.sendText()。
| ??? public void sendText(String callingPackage, String destAddr, String scAddr, ??????????? String text, PendingIntent sentIntent, PendingIntent deliveryIntent, ??????????? boolean persistMessageForNonDefaultSmsApp) { ???????//檢查是否聲明了發短信的權限 ??????? mPhone.getContext().enforceCallingPermission( ??????????????? Manifest.permission.SEND_SMS, ??????????????? "Sending SMS message"); ????????sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, ????????????persistMessageForNonDefaultSmsApp);//轉調了內部方法 ??? } |
轉調了內部方法sendTextInternal()。
| private void sendTextInternal(String callingPackage, String destAddr, String scAddr, ??????????? String text, PendingIntent sentIntent, PendingIntent deliveryIntent, ??????????? boolean persistMessageForNonDefaultSmsApp) { ??????? if (Rlog.isLoggable("SMS", Log.VERBOSE)) { ??????????? log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + ??????????????? " text='"+ text + "' sentIntent=" + ??????????????? sentIntent + " deliveryIntent=" + deliveryIntent); ??????? } ???????//檢查該操作是否被用戶允許 ??????? if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), ??????????????? callingPackage) != AppOpsManager.MODE_ALLOWED) { ??????????? return; ??????? } ??????? if (!persistMessageForNonDefaultSmsApp) { ????????????// Only allow carrier app to skip auto message persistence. ??????????? enforceCarrierPrivilege(); ??????? } ??????? destAddr = filterDestAddress(destAddr);//對目的地址進行檢測 ????????mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, ???????????? ???null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp); ??? } |
其實就是對短信的權限和目的地址的有效性進行了篩查。然后進行短信的Dispatch。
1.2???SmsDispatcher
SmsDispatcher總共派生出三個子類:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher,在IccSmsInterfaceManager創建時只創建ImsSMSDispatcher,而在ImsSmsDispatcher創建過程中會對創建其他兩種制式的SmsDispatcher,IccSmsInterfaceManager把請求發送給ImsSMSDispatcher后,由ImsSMSDispatcher根據當前網絡狀態選擇使用CdmaSmsDispatcher還是GsmSmsDispatcher。這里主要以Cdma為例。因此調用的是CdmaSmsDispathcer.sendText()。
| ??? protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, ??????????? PendingIntent deliveryIntent, Uri messageUri, String callingPkg, ??????????? boolean persistMessage) { ????????//將短信內容包裝成pdu ????????SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( ????? ??????????scAddr, destAddr, text, (deliveryIntent != null), null); ??????? if (pdu != null) { ????????????//接著將短信包裝成tracker ??????????? HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); ????????????SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage); ????????????//carrier是運營商的意思,因此這里和運營商有關 ????????????String carrierPackage = getCarrierAppPackageName(); ??????????? if (carrierPackage != null) {//通過運行商的app發送短信 ??????????????? Rlog.d(TAG, "Found carrier package."); ??????????????? TextSmsSender smsSender = new TextSmsSender(tracker); ??????????????? smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); ??????????? } else { ??????????????? Rlog.v(TAG, "No carrier package."); ????????????????sendSubmitPdu(tracker);//一般走這里 ??????????? } ??????? } else { ??????????? Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null"); ??????????? if (sentIntent != null) { ??????????????? try { ??????????????????? sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); ??????????????? } catch (CanceledException ex) { ??????????????????? Rlog.e(TAG, "Intent has been canceled!"); ??????????????? } ??????????? } ??????? } ?? ?} |
進入sendSubmitPdu()
| ??? protected void sendSubmitPdu(SmsTracker tracker) { ????????//緊急回撥模式檢測 ??????? if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) { ??????????? if (VDBG) { ??????????????? Rlog.d(TAG, "Block SMS in Emergency Callback mode"); ??????????? } ??????????? tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); ??????????? return; ??????? } ????????sendRawPdu(tracker);//這里 ??? } |
進入sendRawPdu()
| protected void sendRawPdu(SmsTracker tracker) { ??????? HashMap map = tracker.mData;??//從tracker中解析出map ????????byte pdu[] = (byte[]) map.get("pdu");//從map中解析出pdu ? ??????? if (mSmsSendDisabled) {//短信發送被禁止了 ??????????? Rlog.e(TAG, "Device does not support sending sms."); ??????????? tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); ??????????? return; ??????? } ? ??????? if (pdu == null) {//pdu空 ??????????? Rlog.e(TAG, "Empty PDU"); ??????????? tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/); ??????????? return; ??? ????} ? ????????// Get calling app package name via UID from Binder call ??????? PackageManager pm = mContext.getPackageManager(); ??????? String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); ? ??????? if (packageNames == null || packageNames.length == 0) { ????????????// Refuse to send SMS if we can't get the calling package name. ??????????? Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS"); ??????????? tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); ??????????? return; ??????? } ? ????????// Get package info via packagemanager ??????? PackageInfo appInfo; ??????? try { ????????????// XXX this is lossy- apps can share a UID ??????????? appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); ??????? } catch (PackageManager.NameNotFoundException e) { ??????????? Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"); ??????????? tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); ???????? ???return; ??????? } ? ????????// checkDestination() returns true if the destination is not a premium short code or the ??????? // sending app is approved to send to short codes. Otherwise, a message is sent to our ??????? // handler with the SmsTracker to request user confirmation before sending. ??????? if (checkDestination(tracker)) { ????????????// check for excessive(過多的)?outgoing SMS usage by this app ??????????? if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { ??????????????? sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); ??????????????? return; ??????????? } ????????? ??sendSms(tracker);//這里 ??????? } ??? } |
進入sendSms()。
| ??? protected void sendSms(SmsTracker tracker) { ??????? HashMap<String, Object> map = tracker.mData; ? ????????// byte[] smsc = (byte[]) map.get("smsc");? // unused for CDMA ??????? byte[] pdu = (byte[]) map.get("pdu");//再次從tracker中解出pdu ? ??????? Rlog.d(TAG, "sendSms: " ??????????????? + " isIms()=" + isIms() ??????????????? + " mRetryCount=" + tracker.mRetryCount ??????????????? + " mImsRetry=" + tracker.mImsRetry ??????????????? + " mMessageRef=" + tracker.mMessageRef ??????????????? + " SS=" + mPhone.getServiceState().getState()); ? ????????sendSmsByPstn(tracker);//這里 ??? } |
進入sendSmsByPstn().
| ??? protected void sendSmsByPstn(SmsTracker tracker) { ??????? int ss = mPhone.getServiceState().getState();//獲取phone狀態 ????????// if sms over IMS is not supported on data and voice is not available... ??????? if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { ??????????? tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); ??????????? return; ??????? } ????????//獲取一個發送完成的Message,一遍發送完成后回到HandleMessage ????????Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); ????????byte[] pdu = (byte[]) tracker.mData.get("pdu");//又解出pdu,好頻繁啊 ? ??????? int currentDataNetwork = mPhone.getServiceState().getDataNetworkType(); ??????? boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD ??????????????????? || (currentDataNetwork == TelephonyManager.NETWORK_TYPE_LTE ??????????????????? && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed())) ??????????????????? && mPhone.getServiceState().getVoiceNetworkType() ??????????????????? == TelephonyManager.NETWORK_TYPE_1xRTT ??????????????????? && ((CDMAPhone) mPhone).mCT.mState != PhoneConstants.State.IDLE; ? ????? ??// sms over cdma is used: ??????? //?? if sms over IMS is not supported AND ??????? //?? this is not a retry case after sms over IMS failed ??????? //???? indicated by mImsRetry > 0 ??????? //注意攜帶了參數reply是一個EVENT_SEND_SMS_COMPLETE, tracker的消息 ??????? if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) { ????????????mCi.sendCdmaSms(pdu, reply); ??????? } else { ????????????mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply); ????????????// increment it here, so in case of SMS_FAIL_RETRY over IMS ??????????? // next retry will be sent using IMS request again. ??????????? tracker.mImsRetry++;//如果是重試,重試次數加1 ??????? } ??? } |
mCi我們在phone應用的分析中重點分析過,其實就是RILJ。因此進入到了RIL層。關于RIL在phone應用的分析中已經很詳細了,肯定是先構造一個RILRequest,然后將pdu數據寫入,接著send(rr),接著在processSolicited中處理發送結果,并調用rr.mResult.sendToTarget()將結果上傳到到上層(這里是smsDispatcher)。這里對于RILJ以下的處理過程就不贅述了。
當RILJ發送完畢,reply消息被發送,因此sms發送成功的消息被smsDispather接收并HandleMessage。在handleMessage()中直接調用了handleSendComplete()方法。
| ??? protected void handleSendComplete(AsyncResult ar) { ????????SmsTracker tracker = (SmsTracker) ar.userObj;//從ar中解出tracker ????????PendingIntent sentIntent = tracker.mSentIntent;//從tracker中解出sendIntent ? ??????? if (ar.result != null) {//解出回應消息 ??????????? tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; ??????? } else { ??????????? Rlog.d(TAG, "SmsResponse was null"); ??????? } ??????? if (ar.exception == null) {//如果沒有異常,表示發送成功 ??????????? if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent); //如果需要等待對方接受的結果狀態,將tracker添加到pendinglist以等待結果 //需要注意的是這里的mSendItent和mDeliveryIntent都是pendingIntent,就是留待以后觸發的意思,需要觸發是調用PendingIntent.send()-—網絡總結 ??????????? if (tracker.mDeliveryIntent != null) { ????????????????// Expecting a status report.? Add it to the list. ????????????????deliveryPendingList.add(tracker);//留待以后觸發 ??????????? } ????????????tracker.onSent(mContext);//發送消息廣播,內部調用了PendingIntent.send() ??????? } else {//如果有異常,表示發送短信失敗 ??????????? if (DBG) Rlog.d(TAG, "SMS send failed"); ????????????//首先獲取短信的狀態 ????????????int ss = mPhone.getServiceState().getState(); ????????????//短信發送失敗,可以重試,但服務不再Service狀態,直接將重試次數設到超過最大 ??????????? if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { ????????????????// This is retry after failure over IMS but voice is not available. ??????????????? // Set retry to max allowed, so no retry is sent and ??????????????? //?? cause RESULT_ERROR_GENERIC_FAILURE to be returned to app. ??????????????? tracker.mRetryCount = MAX_SEND_RETRIES;//設置最大重試次數,即不重試 ? ??????????????? Rlog.d(TAG, "handleSendComplete: Skipping retry: " ??????????????? +" isIms()="+isIms() ??????????????? +" mRetryCount="+tracker.mRetryCount ??????????????? +" mImsRetry="+tracker.mImsRetry ??????????????? +" mMessageRef="+tracker.mMessageRef ??????????????? +" SS= "+mPhone.getServiceState().getState()); ??????????? } ????????????// if sms over IMS is not supported on data and voice is not available... ??????????? if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { ??????????????? tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); ??????????? } else if ((((CommandException)(ar.exception)).getCommandError() ??????????????????? == CommandException.Error.SMS_FAIL_RETRY) && ???????????????? ??tracker.mRetryCount < MAX_SEND_RETRIES) { ????????????????//發送失敗,重試,從這里看出重試次數有次數限制 ??????????????? tracker.mRetryCount++; ??????????????? Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); ??????????????? sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); ??????????? } else { ????????????????int errorCode = 0;//默認沒有errorcode ??????????????? if (ar.result != null) {//根據ar設置errorcode ????????????????????errorCode = ((SmsResponse)ar.result).mErrorCode; ????????? ??????} ????????????????int error = RESULT_ERROR_GENERIC_FAILURE;//默認錯誤為這個 ??????????????? if (((CommandException)(ar.exception)).getCommandError() ??????????????????????? == CommandException.Error.FDN_CHECK_FAILURE) { ???????????????????//如果底層特殊上報了error,則根據底層設置error ????????????????????error = RESULT_ERROR_FDN_CHECK_FAILURE; ??????????????? } ???????//將錯誤及錯誤code發送到上層,內部同樣調用了PendingIntent.send()觸發執行Intent ????????????????tracker.onFailed(mContext, error, errorCode); ??????????? } ??????? } ??? } |
可以看到對于短信的發送失敗和成功狀態的處理,最后都是通過SmsTracker來處理的。總結RILJ之上的短信發送過程如下圖所示。
1.3???SmsTracker與pendingItent
這里主要涉及到了PendingIntent,因此還有待研究!
1.4???小結
下圖是普通短信的處理流程。可以看到的是,在上層,短信是通過源目地址以及String等體現出來的,接著往底層走是tracker,再接著到RILJ演變成了pdu數據,再到RILRequest下發到RILD。
?
在圖中也畫出了長短信的處理流程,可以看到長短信的處理與普通短信的處理基本類似,僅僅是多了分段處理(在App中)。
原文地址: http://blog.csdn.net/a34140974/article/details/50964080
總結
以上是生活随笔為你收集整理的Android6.0的SMS(短信)源码分析--短信发送的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android6.0的Looper源码分
- 下一篇: Android6.0的SMS(短信)源码