Android开发之Android5.1.1(CM12.1)源码中短信发送流程解析

来源:互联网 发布:figma软件 编辑:程序博客网 时间:2024/05/15 15:47

首先我要从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()方法。 
判断网络制式:

 /**  * Determines whether or not to use CDMA format for MO SMS. If SMS over IMS  * is supported, then format is based on IMS SMS format, otherwise format is  * based on current phone type.  *  * @return true if Cdma format should be used for MO SMS, false otherwise.  */ private boolean isCdmaMo() {      if (!isIms() || !shouldSendSmsOverIms())  {   // Either IMS is not registered or there is an active 1x voice call   // while on eHRPD, use Voice technology to determine SMS format.   return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());  }  // IMS is registered with SMS support  return isCdmaFormat(mImsSmsFormat); }
  • 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()方法,如下:

@Override protected 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 call  PackageManager 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 exists  String[] 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 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);  } }
  • 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()方法:

@Override protected void sendSms(SmsTracker tracker) {  HashMap<String, Object> map = tracker.mData;  byte pdu[] = (byte[]) map.get("pdu");  if (tracker.mRetryCount > 0)  {   Rlog.d(TAG, "sendSms: " + " mRetryCount=" + tracker.mRetryCount     + " mMessageRef=" + tracker.mMessageRef + " SS="     + mPhone.getServiceState().getState());   // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type   // TP-RD (bit 2) is 1 for retry   // and TP-MR is set to previously failed sms TP-MR   if (((0x01 & pdu[0]) == 0x01))   {    pdu[0] |= 0x04; // TP-RD    pdu[1] = (byte) tracker.mMessageRef; // TP-MR   }  }  Rlog.d(TAG, "sendSms: " + " isIms()=" + isIms() + " mRetryCount="    + tracker.mRetryCount + " mImsRetry=" + tracker.mImsRetry    + " mMessageRef=" + tracker.mMessageRef + " SS="    + mPhone.getServiceState().getState());  sendSmsByPstn(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
  • 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()方法又干了些什么事情。

@Override protected void sendSmsByPstn(SmsTracker tracker) {  int 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 */);   return;  }  HashMap<String, Object> map = tracker.mData;  byte smsc[] = (byte[]) map.get("smsc");  byte[] pdu = (byte[]) map.get("pdu");  Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);  // sms over gsm 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  if (0 == tracker.mImsRetry && !isIms())  {   if (tracker.mRetryCount > 0)   {    // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01)    // type    // TP-RD (bit 2) is 1 for retry    // and TP-MR is set to previously failed sms TP-MR    if (((0x01 & pdu[0]) == 0x01))    {     pdu[0] |= 0x04; // TP-RD     pdu[1] = (byte) tracker.mMessageRef; // TP-MR    }   }   if (tracker.mRetryCount == 0 && tracker.mExpectMore)   {    mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),      IccUtils.bytesToHexString(pdu), reply);   } else   {    mCi.sendSMS(IccUtils.bytesToHexString(smsc),      IccUtils.bytesToHexString(pdu), reply);   }  } else  {   mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),     IccUtils.bytesToHexString(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
  • 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

0 0
原创粉丝点击