Android开发之Android5.1.1(CM12.1)源码中短信发送流程解析
首先我要從SmsManager開始一步步深入了解,相信大家在學習Android基礎的時候接觸過這個類。它在/frameworks/opt/telephony/src/java/android/telephony路徑下,SmsManager:提供管理短信操作,如發送數據,文本和PDU短信。通過調用靜態方法SmsManager.getDefault() 獲取此對象。它里面提供了一系列發送短信的方法,我們就從sendTextMessage()方法說起,首先我們來看看這個方法:
public void sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {if (TextUtils.isEmpty(destinationAddress)){throw new IllegalArgumentException("Invalid destinationAddress");}if (TextUtils.isEmpty(text)){throw new IllegalArgumentException("Invalid message body");}try{ISms iccISms = getISmsServiceOrThrow();iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),destinationAddress,scAddress, text, sentIntent, deliveryIntent);} catch (RemoteException ex){// ignore it} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
先判斷地址和短信內容是否為空,并且拋出異常信息,然后通過ISms這樣一個東西調用sendTextForSubscriber()方法將短信往下一個類進行傳遞。
getISmsServiceOrThrow():獲取ISms服務。?
destinationAddress:收短信人的地址。?
scAddress:短信號碼中心,如果傳null則為默認短信號碼中心。?
text:短信內容。?
sentIntent:短信發送成功或者失敗的廣播。?
deliveryIntent:對方收到短信時候的廣播。?
getSubscriptionId():獲取訂閱id。?
ActivityThread.currentPackageName():當前的包名。
在上面接觸到ISms這樣一個東西,那么他是干嘛的呢?其實它一個接口,在frameworks/base/telephony/java/com/android/internal/telephony下面,我們會找到它,會發現它是是一個aidl文件,打開它是一個接口(interface),這是我們就要去找另外一個類了,在android5.1.1中有能力完成短信發送任務的系統服務它就是UiccSmsController.java。它在/frameworks/opt/telephony/src/java/com/android/internal/telephony路徑下面,UiccSmsController:提供一個進程間通信訪問ICC中的短信。打開它會發現這樣的繼承關系:
public class UiccSmsController extends ISms.Stub- 1
- 1
里面有這樣的一段代碼:
public void sendText(String callingPackage, String destAddr, String scAddr,String text, PendingIntent sentIntent, PendingIntent deliveryIntent){sendTextForSubscriber(getDefaultSmsSubId(), callingPackage, destAddr,scAddr, text, sentIntent, deliveryIntent);}public void sendTextForSubscriber(int subId, String callingPackage,String destAddr, String scAddr,String text,PendingIntent sentIntent, PendingIntent deliveryIntent){sendTextWithOptionsUsingSubscriber(subId, callingPackage, destAddr,scAddr, text, sentIntent, deliveryIntent, -1, false, -1);}public void sendTextWithOptionsUsingSubscriber(int subId,String callingPackage, String destAddr,String scAddr, String text,PendingIntent sentIntent, PendingIntent deliveryIntent,int priority, boolean isExpectMore, int validityPeriod){mContext.enforceCallingPermission(android.Manifest.permission.SEND_SMS,"Sending SMS message");IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);if (iccSmsIntMgr.isShortSMSCode(destAddr)){iccSmsIntMgr.sendTextWithOptions(callingPackage, destAddr, scAddr,text, sentIntent, deliveryIntent, priority, isExpectMore,validityPeriod);return;}ArrayList<String> parts = new ArrayList<String>();parts.add(text);ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();sentIntents.add(sentIntent);ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();deliveryIntents.add(deliveryIntent);broadcastOutgoingSms(subId, callingPackage, destAddr, scAddr, false,parts, sentIntents, deliveryIntents, priority, isExpectMore,validityPeriod);}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
可以看出現進行權限的操作,然后調用了IccSmsInterfaceManager 的sendTextWithOptions方法將短信進一步傳遞,后面進行廣播的處理。通過getDefaultSmsSubId()獲得了一個手機卡的默認SubId,在同級路徑下找到IccSmsInterfaceManager 類打開之后,又會發現,它調用SMSDispatcher的sendText()方法將短信進一步傳遞。在同級路徑下我們打開SMSDispatcher 會發現它是一個抽象類,并且繼承了Handler。如下:
public abstract class SMSDispatcher extends Handler- 1
- 1
既然是抽象類,那肯定就有實現它的派生類,在Android5.1.1中我找到了三個派生類:CdmaSMSDispatcher、GsmSMSDispatcher、ImsSmsDispatcher。但是在IccSmsInterfaceManager 中只創建了ImsSmsDispatcher。
protected IccSmsInterfaceManager(PhoneBase phone){mPhone = phone;mContext = phone.getContext();mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);mDispatcher = new ImsSMSDispatcher(phone, phone.mSmsStorageMonitor,phone.mSmsUsageMonitor);}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在同級路徑下打開ImsSmsDispatcher.java我們會發現,ImsSmsDispatcher持有了CdmaSMSDispatcher、GsmSMSDispatcher這兩個對象的實例:
private SMSDispatcher mCdmaDispatcher; private SMSDispatcher mGsmDispatcher;- 1
- 2
- 1
- 2
通過判斷網絡制式,分別調用mCdmaDispatcher或者mGsmDispatcher的sendText()方法。?
判斷網絡制式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
這兒我們以mGsmDispatcher作為下一步講解對象,繼續研究短信的發送。在frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm路徑下打開GsmSMSDispatcher,找到sendText()方法,如下:
@Overrideprotected void sendText(String destAddr, String scAddr, String text,PendingIntent sentIntent,PendingIntent deliveryIntent,Uri messageUri, String callingPkg, int priority,boolean isExpectMore, int validityPeriod){SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddr, destAddr,text,(deliveryIntent != null), validityPeriod);if (pdu != null){HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent,getFormat(),messageUri, isExpectMore,text /* fullMessageText */, true /* isText */,validityPeriod);String carrierPackage = getCarrierAppPackageName();if (carrierPackage != null){Rlog.d(TAG, "Found carrier package.");TextSmsSender smsSender = new TextSmsSender(tracker);smsSender.sendSmsByCarrierApp(carrierPackage,new SmsSenderCallback(smsSender));} else{Rlog.v(TAG, "No carrier package.");sendRawPdu(tracker);}} else{Rlog.e(TAG,"GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");}}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
我們會發現,首先將短信組裝成pdu格式,然后進一步將pdu組裝成為一個SmsTracker ,getCarrierAppPackageName():獲取手機內置的載體app,一般手機廠商都不會內置這個,所以一般都會走sendRawPdu()這個方法。這個方法在父類SMSDispatcher 中,所以我們又得去父類看:
protected void sendRawPdu(SmsTracker tracker){HashMap map = tracker.mData;byte pdu[] = (byte[]) map.get("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){Rlog.e(TAG, "Empty PDU");tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/* errorCode */);return;}PendingIntent sentIntent = tracker.mSentIntent;// Get calling app package name via UID from Binder callPackageManager pm = mContext.getPackageManager();int callingUid = Binder.getCallingUid();// Special case: We're being proxied by the telephony stack itself,// so use the intent generator's UID if one existsString[] packageNames;if (callingUid == android.os.Process.PHONE_UID && sentIntent != null&& sentIntent.getCreatorPackage() != null){packageNames = new String[]{ sentIntent.getCreatorPackage() };} else{packageNames = pm.getPackagesForUid(callingUid);}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 packagemanagerPackageInfo appInfo;try{// XXX this is lossy- apps can share a UIDappInfo = 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 appif (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)){sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));return;}sendSms(tracker);}}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
從上面可以得知,先從tracker中取出data,之后再取出pdu。?
判斷短信發送是否可用?
pdu是否為空。?
checkDestination(tracker):判斷內置的short code和發送的short code是否一致。?
然后調用GsmSMSDispatcher的sendSms()方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
可以看到上面需要對短信進行一個處理,判斷重試次數,如果大于0就將第一個字節賦值為0x04 至于為什么是0x04我也不知道,?
TP-RD:是否拒絕相同重復消息。?
TP-MR:消息基準值。?
現在來看GsmSMSDispatcher的sendSmsByPstn()方法又干了些什么事情。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
先是對手機狀態進行判斷,然后又將pdu取出來,然后弄了一個發送完成的消息。?
isIms():判斷IMS是否注冊,和SMS是否支持。自此就開始調用mCi了,也就是CommandsInterface類了,這個類在/frameworks/opt/telephony/src/java/com/android/internal/telephony路徑下,這個就進入額RIL層了,也就不是我研究的范圍了。其實在我修改過中是沒有修改到這一層,基本修改操作基本都是在調用sendRawPdu()方法之前完成。是不是已經腦殼都看糊了?好了,下面我們來看一張圖,總結一下,短信發送的流程!?
自此,希望大家能對短信的發送有個大體的認識,如果需要詳細的了解短信發送的每個細節,可以下載源代碼觀看。結合源代碼,觀看本文效果更佳!
原文地址: http://blog.csdn.net/poison_h/article/details/50972149
總結
以上是生活随笔為你收集整理的Android开发之Android5.1.1(CM12.1)源码中短信发送流程解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 代码优化工具FindBu
- 下一篇: Android短信发送流程之普通短信发送